diff options
-rw-r--r-- | net/netfilter/ipvs/Kconfig | 7 | ||||
-rw-r--r-- | net/netfilter/ipvs/Makefile | 3 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_pe_sip.c | 167 |
3 files changed, 177 insertions, 0 deletions
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index af3c9f48f2d7..a22dac227055 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig | |||
@@ -256,4 +256,11 @@ config IP_VS_NFCT | |||
256 | connection state to be exported to the Netfilter framework | 256 | connection state to be exported to the Netfilter framework |
257 | for filtering purposes. | 257 | for filtering purposes. |
258 | 258 | ||
259 | config IP_VS_PE_SIP | ||
260 | tristate "SIP persistence engine" | ||
261 | depends on IP_VS_PROTO_UDP | ||
262 | depends on NF_CONNTRACK_SIP | ||
263 | ---help--- | ||
264 | Allow persistence based on the SIP Call-ID | ||
265 | |||
259 | endif # IP_VS | 266 | endif # IP_VS |
diff --git a/net/netfilter/ipvs/Makefile b/net/netfilter/ipvs/Makefile index 4a87bf3c6293..34ee602ddb66 100644 --- a/net/netfilter/ipvs/Makefile +++ b/net/netfilter/ipvs/Makefile | |||
@@ -35,3 +35,6 @@ obj-$(CONFIG_IP_VS_NQ) += ip_vs_nq.o | |||
35 | 35 | ||
36 | # IPVS application helpers | 36 | # IPVS application helpers |
37 | obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o | 37 | obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o |
38 | |||
39 | # IPVS connection template retrievers | ||
40 | obj-$(CONFIG_IP_VS_PE_SIP) += ip_vs_pe_sip.o | ||
diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c new file mode 100644 index 000000000000..a0539f13e2b4 --- /dev/null +++ b/net/netfilter/ipvs/ip_vs_pe_sip.c | |||
@@ -0,0 +1,167 @@ | |||
1 | #define KMSG_COMPONENT "IPVS" | ||
2 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
3 | |||
4 | #include <linux/module.h> | ||
5 | #include <linux/kernel.h> | ||
6 | |||
7 | #include <net/ip_vs.h> | ||
8 | #include <net/netfilter/nf_conntrack.h> | ||
9 | #include <linux/netfilter/nf_conntrack_sip.h> | ||
10 | |||
11 | static const char *ip_vs_dbg_callid(char *buf, size_t buf_len, | ||
12 | const char *callid, size_t callid_len, | ||
13 | int *idx) | ||
14 | { | ||
15 | size_t len = min(min(callid_len, (size_t)64), buf_len - *idx - 1); | ||
16 | memcpy(buf + *idx, callid, len); | ||
17 | buf[*idx+len] = '\0'; | ||
18 | *idx += len + 1; | ||
19 | return buf + *idx - len; | ||
20 | } | ||
21 | |||
22 | #define IP_VS_DEBUG_CALLID(callid, len) \ | ||
23 | ip_vs_dbg_callid(ip_vs_dbg_buf, sizeof(ip_vs_dbg_buf), \ | ||
24 | callid, len, &ip_vs_dbg_idx) | ||
25 | |||
26 | static int get_callid(const char *dptr, unsigned int dataoff, | ||
27 | unsigned int datalen, | ||
28 | unsigned int *matchoff, unsigned int *matchlen) | ||
29 | { | ||
30 | /* Find callid */ | ||
31 | while (1) { | ||
32 | int ret = ct_sip_get_header(NULL, dptr, dataoff, datalen, | ||
33 | SIP_HDR_CALL_ID, matchoff, | ||
34 | matchlen); | ||
35 | if (ret > 0) | ||
36 | break; | ||
37 | if (!ret) | ||
38 | return 0; | ||
39 | dataoff += *matchoff; | ||
40 | } | ||
41 | |||
42 | /* Empty callid is useless */ | ||
43 | if (!*matchlen) | ||
44 | return -EINVAL; | ||
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 | |||
73 | ip_vs_fill_iphdr(p->af, skb_network_header(skb), &iph); | ||
74 | |||
75 | /* Only useful with UDP */ | ||
76 | if (iph.protocol != IPPROTO_UDP) | ||
77 | return -EINVAL; | ||
78 | |||
79 | /* No Data ? */ | ||
80 | dataoff = iph.len + sizeof(struct udphdr); | ||
81 | if (dataoff >= skb->len) | ||
82 | return -EINVAL; | ||
83 | |||
84 | dptr = skb->data + dataoff; | ||
85 | datalen = skb->len - dataoff; | ||
86 | |||
87 | if (get_callid(dptr, dataoff, datalen, &matchoff, &matchlen)) | ||
88 | return -EINVAL; | ||
89 | |||
90 | p->pe_data = kmalloc(matchlen, GFP_ATOMIC); | ||
91 | if (!p->pe_data) | ||
92 | return -ENOMEM; | ||
93 | |||
94 | /* N.B: pe_data is only set on success, | ||
95 | * this allows fallback to the default persistence logic on failure | ||
96 | */ | ||
97 | memcpy(p->pe_data, dptr + matchoff, matchlen); | ||
98 | p->pe_data_len = matchlen; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static bool ip_vs_sip_ct_match(const struct ip_vs_conn_param *p, | ||
104 | struct ip_vs_conn *ct) | ||
105 | |||
106 | { | ||
107 | bool ret = 0; | ||
108 | |||
109 | if (ct->af == p->af && | ||
110 | ip_vs_addr_equal(p->af, p->caddr, &ct->caddr) && | ||
111 | /* protocol should only be IPPROTO_IP if | ||
112 | * d_addr is a fwmark */ | ||
113 | ip_vs_addr_equal(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af, | ||
114 | p->vaddr, &ct->vaddr) && | ||
115 | ct->vport == p->vport && | ||
116 | ct->flags & IP_VS_CONN_F_TEMPLATE && | ||
117 | ct->protocol == p->protocol && | ||
118 | ct->pe_data && ct->pe_data_len == p->pe_data_len && | ||
119 | !memcmp(ct->pe_data, p->pe_data, p->pe_data_len)) | ||
120 | ret = 1; | ||
121 | |||
122 | IP_VS_DBG_BUF(9, "SIP template match %s %s->%s:%d %s\n", | ||
123 | ip_vs_proto_name(p->protocol), | ||
124 | IP_VS_DEBUG_CALLID(p->pe_data, p->pe_data_len), | ||
125 | IP_VS_DBG_ADDR(p->af, p->vaddr), ntohs(p->vport), | ||
126 | ret ? "hit" : "not hit"); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static u32 ip_vs_sip_hashkey_raw(const struct ip_vs_conn_param *p, | ||
132 | u32 initval, bool inverse) | ||
133 | { | ||
134 | return jhash(p->pe_data, p->pe_data_len, initval); | ||
135 | } | ||
136 | |||
137 | static int ip_vs_sip_show_pe_data(const struct ip_vs_conn *cp, char *buf) | ||
138 | { | ||
139 | memcpy(buf, cp->pe_data, cp->pe_data_len); | ||
140 | return cp->pe_data_len; | ||
141 | } | ||
142 | |||
143 | static struct ip_vs_pe ip_vs_sip_pe = | ||
144 | { | ||
145 | .name = "sip", | ||
146 | .refcnt = ATOMIC_INIT(0), | ||
147 | .module = THIS_MODULE, | ||
148 | .n_list = LIST_HEAD_INIT(ip_vs_sip_pe.n_list), | ||
149 | .fill_param = ip_vs_sip_fill_param, | ||
150 | .ct_match = ip_vs_sip_ct_match, | ||
151 | .hashkey_raw = ip_vs_sip_hashkey_raw, | ||
152 | .show_pe_data = ip_vs_sip_show_pe_data, | ||
153 | }; | ||
154 | |||
155 | static int __init ip_vs_sip_init(void) | ||
156 | { | ||
157 | return register_ip_vs_pe(&ip_vs_sip_pe); | ||
158 | } | ||
159 | |||
160 | static void __exit ip_vs_sip_cleanup(void) | ||
161 | { | ||
162 | unregister_ip_vs_pe(&ip_vs_sip_pe); | ||
163 | } | ||
164 | |||
165 | module_init(ip_vs_sip_init); | ||
166 | module_exit(ip_vs_sip_cleanup); | ||
167 | MODULE_LICENSE("GPL"); | ||