1 // SPDX-License-Identifier: GPL-2.0-only 2 #define KMSG_COMPONENT "IPVS" 3 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 4 5 #include <linux/module.h> 6 #include <linux/kernel.h> 7 8 #include <net/ip_vs.h> 9 #include <net/netfilter/nf_conntrack.h> 10 #include <linux/netfilter/nf_conntrack_sip.h> 11 12 #ifdef CONFIG_IP_VS_DEBUG 13 static const char *ip_vs_dbg_callid(char *buf, size_t buf_len, 14 const char *callid, size_t callid_len, 15 int *idx) 16 { 17 size_t max_len = 64; 18 size_t len = min3(max_len, callid_len, buf_len - *idx - 1); 19 memcpy(buf + *idx, callid, len); 20 buf[*idx+len] = '\0'; 21 *idx += len + 1; 22 return buf + *idx - len; 23 } 24 25 #define IP_VS_DEBUG_CALLID(callid, len) \ 26 ip_vs_dbg_callid(ip_vs_dbg_buf, sizeof(ip_vs_dbg_buf), \ 27 callid, len, &ip_vs_dbg_idx) 28 #endif 29 30 static int get_callid(const char *dptr, unsigned int dataoff, 31 unsigned int datalen, 32 unsigned int *matchoff, unsigned int *matchlen) 33 { 34 /* Find callid */ 35 while (1) { 36 int ret = ct_sip_get_header(NULL, dptr, dataoff, datalen, 37 SIP_HDR_CALL_ID, matchoff, 38 matchlen); 39 if (ret > 0) 40 break; 41 if (!ret) 42 return -EINVAL; 43 dataoff += *matchoff; 44 } 45 46 /* Too large is useless */ 47 if (*matchlen > IP_VS_PEDATA_MAXLEN) 48 return -EINVAL; 49 50 /* SIP headers are always followed by a line terminator */ 51 if (*matchoff + *matchlen == datalen) 52 return -EINVAL; 53 54 /* RFC 2543 allows lines to be terminated with CR, LF or CRLF, 55 * RFC 3261 allows only CRLF, we support both. */ 56 if (*(dptr + *matchoff + *matchlen) != '\r' && 57 *(dptr + *matchoff + *matchlen) != '\n') 58 return -EINVAL; 59 60 IP_VS_DBG_BUF(9, "SIP callid %s (%d bytes)\n", 61 IP_VS_DEBUG_CALLID(dptr + *matchoff, *matchlen), 62 *matchlen); 63 return 0; 64 } 65 66 static int 67 ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) 68 { 69 struct ip_vs_iphdr iph; 70 unsigned int dataoff, datalen, matchoff, matchlen; 71 const char *dptr; 72 int retc; 73 74 retc = ip_vs_fill_iph_skb(p->af, skb, false, &iph); 75 76 /* Only useful with UDP */ 77 if (!retc || iph.protocol != IPPROTO_UDP) 78 return -EINVAL; 79 /* todo: IPv6 fragments: 80 * I think this only should be done for the first fragment. /HS 81 */ 82 dataoff = iph.len + sizeof(struct udphdr); 83 84 if (dataoff >= skb->len) 85 return -EINVAL; 86 retc = skb_linearize(skb); 87 if (retc < 0) 88 return retc; 89 dptr = skb->data + dataoff; 90 datalen = skb->len - dataoff; 91 92 if (get_callid(dptr, 0, datalen, &matchoff, &matchlen)) 93 return -EINVAL; 94 95 /* N.B: pe_data is only set on success, 96 * this allows fallback to the default persistence logic on failure 97 */ 98 p->pe_data = kmemdup(dptr + matchoff, matchlen, GFP_ATOMIC); 99 if (!p->pe_data) 100 return -ENOMEM; 101 102 p->pe_data_len = matchlen; 103 104 return 0; 105 } 106 107 static bool ip_vs_sip_ct_match(const struct ip_vs_conn_param *p, 108 struct ip_vs_conn *ct) 109 110 { 111 bool ret = false; 112 113 if (ct->af == p->af && 114 ip_vs_addr_equal(p->af, p->caddr, &ct->caddr) && 115 /* protocol should only be IPPROTO_IP if 116 * d_addr is a fwmark */ 117 ip_vs_addr_equal(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af, 118 p->vaddr, &ct->vaddr) && 119 ct->vport == p->vport && 120 ct->flags & IP_VS_CONN_F_TEMPLATE && 121 ct->protocol == p->protocol && 122 ct->pe_data && ct->pe_data_len == p->pe_data_len && 123 !memcmp(ct->pe_data, p->pe_data, p->pe_data_len)) 124 ret = true; 125 126 IP_VS_DBG_BUF(9, "SIP template match %s %s->%s:%d %s\n", 127 ip_vs_proto_name(p->protocol), 128 IP_VS_DEBUG_CALLID(p->pe_data, p->pe_data_len), 129 IP_VS_DBG_ADDR(p->af, p->vaddr), ntohs(p->vport), 130 ret ? "hit" : "not hit"); 131 132 return ret; 133 } 134 135 static u32 ip_vs_sip_hashkey_raw(const struct ip_vs_conn_param *p, 136 u32 initval, bool inverse) 137 { 138 return jhash(p->pe_data, p->pe_data_len, initval); 139 } 140 141 static int ip_vs_sip_show_pe_data(const struct ip_vs_conn *cp, char *buf) 142 { 143 memcpy(buf, cp->pe_data, cp->pe_data_len); 144 return cp->pe_data_len; 145 } 146 147 static struct ip_vs_conn * 148 ip_vs_sip_conn_out(struct ip_vs_service *svc, 149 struct ip_vs_dest *dest, 150 struct sk_buff *skb, 151 const struct ip_vs_iphdr *iph, 152 __be16 dport, 153 __be16 cport) 154 { 155 if (likely(iph->protocol == IPPROTO_UDP)) 156 return ip_vs_new_conn_out(svc, dest, skb, iph, dport, cport); 157 /* currently no need to handle other than UDP */ 158 return NULL; 159 } 160 161 static struct ip_vs_pe ip_vs_sip_pe = 162 { 163 .name = "sip", 164 .refcnt = ATOMIC_INIT(0), 165 .module = THIS_MODULE, 166 .n_list = LIST_HEAD_INIT(ip_vs_sip_pe.n_list), 167 .fill_param = ip_vs_sip_fill_param, 168 .ct_match = ip_vs_sip_ct_match, 169 .hashkey_raw = ip_vs_sip_hashkey_raw, 170 .show_pe_data = ip_vs_sip_show_pe_data, 171 .conn_out = ip_vs_sip_conn_out, 172 }; 173 174 static int __init ip_vs_sip_init(void) 175 { 176 return register_ip_vs_pe(&ip_vs_sip_pe); 177 } 178 179 static void __exit ip_vs_sip_cleanup(void) 180 { 181 unregister_ip_vs_pe(&ip_vs_sip_pe); 182 synchronize_rcu(); 183 } 184 185 module_init(ip_vs_sip_init); 186 module_exit(ip_vs_sip_cleanup); 187 MODULE_LICENSE("GPL"); 188