aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter/nf_conntrack_sip.h49
-rw-r--r--net/ipv4/netfilter/nf_nat_sip.c69
-rw-r--r--net/netfilter/nf_conntrack_sip.c159
3 files changed, 169 insertions, 108 deletions
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index b94de3d60303..9131cbc9b9de 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -13,12 +13,42 @@ enum sip_header_pos {
13 POS_VIA, 13 POS_VIA,
14 POS_CONTACT, 14 POS_CONTACT,
15 POS_CONTENT, 15 POS_CONTENT,
16 POS_MEDIA, 16};
17 POS_OWNER_IP4, 17
18 POS_CONNECTION_IP4, 18struct sip_header {
19 POS_OWNER_IP6, 19 const char *name;
20 POS_CONNECTION_IP6, 20 const char *cname;
21 POS_SDP_HEADER, 21 const char *search;
22 unsigned int len;
23 unsigned int clen;
24 unsigned int slen;
25 int (*match_len)(const struct nf_conn *ct,
26 const char *dptr, const char *limit,
27 int *shift);
28};
29
30#define __SIP_HDR(__name, __cname, __search, __match) \
31{ \
32 .name = (__name), \
33 .len = sizeof(__name) - 1, \
34 .cname = (__cname), \
35 .clen = (__cname) ? sizeof(__cname) - 1 : 0, \
36 .search = (__search), \
37 .slen = (__search) ? sizeof(__search) - 1 : 0, \
38 .match_len = (__match), \
39}
40
41#define SDP_HDR(__name, __search, __match) \
42 __SIP_HDR(__name, NULL, __search, __match)
43
44enum sdp_header_types {
45 SDP_HDR_UNSPEC,
46 SDP_HDR_VERSION,
47 SDP_HDR_OWNER_IP4,
48 SDP_HDR_CONNECTION_IP4,
49 SDP_HDR_OWNER_IP6,
50 SDP_HDR_CONNECTION_IP6,
51 SDP_HDR_MEDIA,
22}; 52};
23 53
24extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, 54extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
@@ -36,5 +66,12 @@ extern int ct_sip_lnlen(const char *line, const char *limit);
36extern const char *ct_sip_search(const char *needle, const char *haystack, 66extern const char *ct_sip_search(const char *needle, const char *haystack,
37 size_t needle_len, size_t haystack_len, 67 size_t needle_len, size_t haystack_len,
38 int case_sensitive); 68 int case_sensitive);
69
70extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
71 unsigned int dataoff, unsigned int datalen,
72 enum sdp_header_types type,
73 enum sdp_header_types term,
74 unsigned int *matchoff, unsigned int *matchlen);
75
39#endif /* __KERNEL__ */ 76#endif /* __KERNEL__ */
40#endif /* __NF_CONNTRACK_SIP_H__ */ 77#endif /* __NF_CONNTRACK_SIP_H__ */
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index dd1b2d86deee..aa8a4f492baf 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -147,51 +147,46 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
147 return NF_ACCEPT; 147 return NF_ACCEPT;
148} 148}
149 149
150static unsigned int mangle_sip_packet(struct sk_buff *skb, 150static int mangle_content_len(struct sk_buff *skb,
151 const char **dptr, unsigned int *datalen, 151 const char **dptr, unsigned int *datalen)
152 char *buffer, int bufflen,
153 enum sip_header_pos pos)
154{ 152{
155 enum ip_conntrack_info ctinfo; 153 enum ip_conntrack_info ctinfo;
156 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 154 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
157 unsigned int matchlen, matchoff; 155 unsigned int matchoff, matchlen;
156 char buffer[sizeof("65536")];
157 int buflen, c_len;
158 158
159 /* Get actual SDP length */
160 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
161 SDP_HDR_VERSION, SDP_HDR_UNSPEC,
162 &matchoff, &matchlen) <= 0)
163 return 0;
164 c_len = *datalen - matchoff + strlen("v=");
165
166 /* Now, update SDP length */
159 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen, 167 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen,
160 pos) <= 0) 168 POS_CONTENT) <= 0)
161 return 0; 169 return 0;
162 170
171 buflen = sprintf(buffer, "%u", c_len);
163 return mangle_packet(skb, dptr, datalen, matchoff, matchlen, 172 return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
164 buffer, bufflen); 173 buffer, buflen);
165} 174}
166 175
167static int mangle_content_len(struct sk_buff *skb, 176static unsigned mangle_sdp_packet(struct sk_buff *skb,
168 const char **dptr, unsigned int *datalen) 177 const char **dptr, unsigned int *datalen,
178 enum sdp_header_types type,
179 char *buffer, int buflen)
169{ 180{
170 enum ip_conntrack_info ctinfo; 181 enum ip_conntrack_info ctinfo;
171 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 182 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
172 unsigned int matchoff, matchlen; 183 unsigned int matchlen, matchoff;
173 char buffer[sizeof("65536")];
174 int bufflen;
175 184
176 /* Get actual SDP length */ 185 if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, type, SDP_HDR_UNSPEC,
177 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, 186 &matchoff, &matchlen) <= 0)
178 &matchlen, POS_SDP_HEADER) > 0) { 187 return 0;
179 188 return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
180 /* since ct_sip_get_info() give us a pointer passing 'v=' 189 buffer, buflen);
181 we need to add 2 bytes in this count. */
182 int c_len = *datalen - matchoff + 2;
183
184 /* Now, update SDP length */
185 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff,
186 &matchlen, POS_CONTENT) > 0) {
187
188 bufflen = sprintf(buffer, "%u", c_len);
189 return mangle_packet(skb, dptr, datalen,
190 matchoff, matchlen,
191 buffer, bufflen);
192 }
193 }
194 return 0;
195} 190}
196 191
197static unsigned int mangle_sdp(struct sk_buff *skb, 192static unsigned int mangle_sdp(struct sk_buff *skb,
@@ -205,18 +200,18 @@ static unsigned int mangle_sdp(struct sk_buff *skb,
205 200
206 /* Mangle owner and contact info. */ 201 /* Mangle owner and contact info. */
207 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); 202 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
208 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen, 203 if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_OWNER_IP4,
209 POS_OWNER_IP4)) 204 buffer, bufflen))
210 return 0; 205 return 0;
211 206
212 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen, 207 if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_CONNECTION_IP4,
213 POS_CONNECTION_IP4)) 208 buffer, bufflen))
214 return 0; 209 return 0;
215 210
216 /* Mangle media port. */ 211 /* Mangle media port. */
217 bufflen = sprintf(buffer, "%u", port); 212 bufflen = sprintf(buffer, "%u", port);
218 if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen, 213 if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_MEDIA,
219 POS_MEDIA)) 214 buffer, bufflen))
220 return 0; 215 return 0;
221 216
222 return mangle_content_len(skb, dptr, datalen); 217 return mangle_content_len(skb, dptr, datalen);
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index cf19a7082a75..801fcb3c749f 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -124,66 +124,6 @@ static const struct sip_header_nfo ct_sip_hdrs[] = {
124 .ln_strlen = sizeof(":") - 1, 124 .ln_strlen = sizeof(":") - 1,
125 .match_len = skp_digits_len 125 .match_len = skp_digits_len
126 }, 126 },
127 [POS_MEDIA] = { /* SDP media info */
128 .case_sensitive = 1,
129 .lname = "\nm=",
130 .lnlen = sizeof("\nm=") - 1,
131 .sname = "\rm=",
132 .snlen = sizeof("\rm=") - 1,
133 .ln_str = "audio ",
134 .ln_strlen = sizeof("audio ") - 1,
135 .match_len = digits_len
136 },
137 [POS_OWNER_IP4] = { /* SDP owner address*/
138 .case_sensitive = 1,
139 .lname = "\no=",
140 .lnlen = sizeof("\no=") - 1,
141 .sname = "\ro=",
142 .snlen = sizeof("\ro=") - 1,
143 .ln_str = "IN IP4 ",
144 .ln_strlen = sizeof("IN IP4 ") - 1,
145 .match_len = epaddr_len
146 },
147 [POS_CONNECTION_IP4] = {/* SDP connection info */
148 .case_sensitive = 1,
149 .lname = "\nc=",
150 .lnlen = sizeof("\nc=") - 1,
151 .sname = "\rc=",
152 .snlen = sizeof("\rc=") - 1,
153 .ln_str = "IN IP4 ",
154 .ln_strlen = sizeof("IN IP4 ") - 1,
155 .match_len = epaddr_len
156 },
157 [POS_OWNER_IP6] = { /* SDP owner address*/
158 .case_sensitive = 1,
159 .lname = "\no=",
160 .lnlen = sizeof("\no=") - 1,
161 .sname = "\ro=",
162 .snlen = sizeof("\ro=") - 1,
163 .ln_str = "IN IP6 ",
164 .ln_strlen = sizeof("IN IP6 ") - 1,
165 .match_len = epaddr_len
166 },
167 [POS_CONNECTION_IP6] = {/* SDP connection info */
168 .case_sensitive = 1,
169 .lname = "\nc=",
170 .lnlen = sizeof("\nc=") - 1,
171 .sname = "\rc=",
172 .snlen = sizeof("\rc=") - 1,
173 .ln_str = "IN IP6 ",
174 .ln_strlen = sizeof("IN IP6 ") - 1,
175 .match_len = epaddr_len
176 },
177 [POS_SDP_HEADER] = { /* SDP version header */
178 .case_sensitive = 1,
179 .lname = "\nv=",
180 .lnlen = sizeof("\nv=") - 1,
181 .sname = "\rv=",
182 .snlen = sizeof("\rv=") - 1,
183 .ln_str = "=",
184 .ln_strlen = sizeof("=") - 1,
185 .match_len = digits_len
186 }
187}; 127};
188 128
189/* get line length until first CR or LF seen. */ 129/* get line length until first CR or LF seen. */
@@ -363,6 +303,92 @@ int ct_sip_get_info(const struct nf_conn *ct,
363} 303}
364EXPORT_SYMBOL_GPL(ct_sip_get_info); 304EXPORT_SYMBOL_GPL(ct_sip_get_info);
365 305
306/* SDP header parsing: a SDP session description contains an ordered set of
307 * headers, starting with a section containing general session parameters,
308 * optionally followed by multiple media descriptions.
309 *
310 * SDP headers always start at the beginning of a line. According to RFC 2327:
311 * "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
312 * be tolerant and also accept records terminated with a single newline
313 * character". We handle both cases.
314 */
315static const struct sip_header ct_sdp_hdrs[] = {
316 [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
317 [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
318 [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
319 [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
320 [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
321 [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
322};
323
324/* Linear string search within SDP header values */
325static const char *ct_sdp_header_search(const char *dptr, const char *limit,
326 const char *needle, unsigned int len)
327{
328 for (limit -= len; dptr < limit; dptr++) {
329 if (*dptr == '\r' || *dptr == '\n')
330 break;
331 if (strncmp(dptr, needle, len) == 0)
332 return dptr;
333 }
334 return NULL;
335}
336
337/* Locate a SDP header (optionally a substring within the header value),
338 * optionally stopping at the first occurence of the term header, parse
339 * it and return the offset and length of the data we're interested in.
340 */
341int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
342 unsigned int dataoff, unsigned int datalen,
343 enum sdp_header_types type,
344 enum sdp_header_types term,
345 unsigned int *matchoff, unsigned int *matchlen)
346{
347 const struct sip_header *hdr = &ct_sdp_hdrs[type];
348 const struct sip_header *thdr = &ct_sdp_hdrs[term];
349 const char *start = dptr, *limit = dptr + datalen;
350 int shift = 0;
351
352 for (dptr += dataoff; dptr < limit; dptr++) {
353 /* Find beginning of line */
354 if (*dptr != '\r' && *dptr != '\n')
355 continue;
356 if (++dptr >= limit)
357 break;
358 if (*(dptr - 1) == '\r' && *dptr == '\n') {
359 if (++dptr >= limit)
360 break;
361 }
362
363 if (term != SDP_HDR_UNSPEC &&
364 limit - dptr >= thdr->len &&
365 strnicmp(dptr, thdr->name, thdr->len) == 0)
366 break;
367 else if (limit - dptr >= hdr->len &&
368 strnicmp(dptr, hdr->name, hdr->len) == 0)
369 dptr += hdr->len;
370 else
371 continue;
372
373 *matchoff = dptr - start;
374 if (hdr->search) {
375 dptr = ct_sdp_header_search(dptr, limit, hdr->search,
376 hdr->slen);
377 if (!dptr)
378 return -1;
379 dptr += hdr->slen;
380 }
381
382 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
383 if (!*matchlen)
384 return -1;
385 *matchoff = dptr - start + shift;
386 return 1;
387 }
388 return 0;
389}
390EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
391
366static int set_expected_rtp(struct sk_buff *skb, 392static int set_expected_rtp(struct sk_buff *skb,
367 const char **dptr, unsigned int *datalen, 393 const char **dptr, unsigned int *datalen,
368 union nf_inet_addr *addr, __be16 port) 394 union nf_inet_addr *addr, __be16 port)
@@ -408,7 +434,7 @@ static int sip_help(struct sk_buff *skb,
408 int ret = NF_ACCEPT; 434 int ret = NF_ACCEPT;
409 unsigned int matchoff, matchlen; 435 unsigned int matchoff, matchlen;
410 u_int16_t port; 436 u_int16_t port;
411 enum sip_header_pos pos; 437 enum sdp_header_types type;
412 typeof(nf_nat_sip_hook) nf_nat_sip; 438 typeof(nf_nat_sip_hook) nf_nat_sip;
413 439
414 /* No Data ? */ 440 /* No Data ? */
@@ -446,8 +472,10 @@ static int sip_help(struct sk_buff *skb,
446 goto out; 472 goto out;
447 } 473 }
448 /* Get address and port from SDP packet. */ 474 /* Get address and port from SDP packet. */
449 pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6; 475 type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
450 if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) { 476 SDP_HDR_CONNECTION_IP6;
477 if (ct_sip_get_sdp_header(ct, dptr, 0, datalen, type, SDP_HDR_UNSPEC,
478 &matchoff, &matchlen) > 0) {
451 479
452 /* We'll drop only if there are parse problems. */ 480 /* We'll drop only if there are parse problems. */
453 if (!parse_addr(ct, dptr + matchoff, NULL, &addr, 481 if (!parse_addr(ct, dptr + matchoff, NULL, &addr,
@@ -455,8 +483,9 @@ static int sip_help(struct sk_buff *skb,
455 ret = NF_DROP; 483 ret = NF_DROP;
456 goto out; 484 goto out;
457 } 485 }
458 if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, 486 if (ct_sip_get_sdp_header(ct, dptr, 0, datalen,
459 POS_MEDIA) > 0) { 487 SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
488 &matchoff, &matchlen) > 0) {
460 489
461 port = simple_strtoul(dptr + matchoff, NULL, 10); 490 port = simple_strtoul(dptr + matchoff, NULL, 10);
462 if (port < 1024) { 491 if (port < 1024) {