aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSimon Horman <horms@verge.net.au>2010-08-22 08:37:55 -0400
committerSimon Horman <horms@verge.net.au>2010-10-04 09:45:24 -0400
commit758ff03387228824617cef9507e5682488bf9e0c (patch)
tree92fa1ff9df6545aa4d3949de9cdcf3ccffe5091c /net
parentf71499aa11f884255b69ce6c3b3c398c821591a1 (diff)
IPVS: sip persistence engine
Add the SIP callid as a key for persistence. This allows multiple connections from the same IP address to be differentiated on the basis of the callid. When used in conjunction with the persistence mask, it allows connections from different IP addresses to be aggregated on the basis of the callid. It is envisaged that a persistence mask of 0.0.0.0 will be a useful setting. That is, ignore the source IP address when checking for persistence. It is envisaged that this option will be used in conjunction with one-packet scheduling. This only works with UDP and cannot be made to work with TCP within the current framework. Signed-off-by: Simon Horman <horms@verge.net.au> Acked-by: Julian Anastasov <ja@ssi.bg>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/ipvs/Kconfig7
-rw-r--r--net/netfilter/ipvs/Makefile3
-rw-r--r--net/netfilter/ipvs/ip_vs_pe_sip.c167
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
259config 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
259endif # IP_VS 266endif # 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
37obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o 37obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o
38
39# IPVS connection template retrievers
40obj-$(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
11static 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
26static 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
66static int
67ip_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
103static 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
131static 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
137static 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
143static 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
155static int __init ip_vs_sip_init(void)
156{
157 return register_ip_vs_pe(&ip_vs_sip_pe);
158}
159
160static void __exit ip_vs_sip_cleanup(void)
161{
162 unregister_ip_vs_pe(&ip_vs_sip_pe);
163}
164
165module_init(ip_vs_sip_init);
166module_exit(ip_vs_sip_cleanup);
167MODULE_LICENSE("GPL");