aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-03-25 23:17:55 -0400
committerDavid S. Miller <davem@davemloft.net>2008-03-25 23:17:55 -0400
commit3e9b4600b4e71beaa9d943251bfe9c25f6a97b8c (patch)
treecb04b3e40f545d104207784982fb55485f231da5
parent779382eb324ad0c39f8c4d10a47a813b490ab82c (diff)
[NETFILTER]: nf_conntrack_sip: add seperate SDP header parsing function
SDP and SIP headers are quite different, SIP can have continuation lines, leading and trailing whitespace after the colon and is mostly case-insensitive while SDP headers always begin on a new line and are followed by an equal sign and the value, without any whitespace. Introduce new SDP header parsing function and convert all users that used the SIP header parsing function. This will allow to properly deal with the special SIP cases in the SIP header parsing function later. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-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 b94de3d6030..9131cbc9b9d 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 dd1b2d86dee..aa8a4f492ba 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 cf19a7082a7..801fcb3c749 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) {