diff options
-rw-r--r-- | include/linux/netfilter/nf_conntrack_sip.h | 49 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_sip.c | 69 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_sip.c | 159 |
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, | 18 | struct 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 | |||
44 | enum 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 | ||
24 | extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, | 54 | extern 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); | |||
36 | extern const char *ct_sip_search(const char *needle, const char *haystack, | 66 | extern 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 | |||
70 | extern 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 | ||
150 | static unsigned int mangle_sip_packet(struct sk_buff *skb, | 150 | static 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 | ||
167 | static int mangle_content_len(struct sk_buff *skb, | 176 | static 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 | ||
197 | static unsigned int mangle_sdp(struct sk_buff *skb, | 192 | static 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 | } |
364 | EXPORT_SYMBOL_GPL(ct_sip_get_info); | 304 | EXPORT_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 | */ | ||
315 | static 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 */ | ||
325 | static 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 | */ | ||
341 | int 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 | } | ||
390 | EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); | ||
391 | |||
366 | static int set_expected_rtp(struct sk_buff *skb, | 392 | static 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) { |