Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 90347
b: refs/heads/master
c: 3e9b460
h: refs/heads/master
i:
  90345: 0a71a90
  90343: ae7d949
v: v3
  • Loading branch information
Patrick McHardy authored and David S. Miller committed Mar 26, 2008
1 parent e1574b5 commit 107f20f
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 109 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 779382eb324ad0c39f8c4d10a47a813b490ab82c
refs/heads/master: 3e9b4600b4e71beaa9d943251bfe9c25f6a97b8c
49 changes: 43 additions & 6 deletions trunk/include/linux/netfilter/nf_conntrack_sip.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,42 @@ enum sip_header_pos {
POS_VIA,
POS_CONTACT,
POS_CONTENT,
POS_MEDIA,
POS_OWNER_IP4,
POS_CONNECTION_IP4,
POS_OWNER_IP6,
POS_CONNECTION_IP6,
POS_SDP_HEADER,
};

struct sip_header {
const char *name;
const char *cname;
const char *search;
unsigned int len;
unsigned int clen;
unsigned int slen;
int (*match_len)(const struct nf_conn *ct,
const char *dptr, const char *limit,
int *shift);
};

#define __SIP_HDR(__name, __cname, __search, __match) \
{ \
.name = (__name), \
.len = sizeof(__name) - 1, \
.cname = (__cname), \
.clen = (__cname) ? sizeof(__cname) - 1 : 0, \
.search = (__search), \
.slen = (__search) ? sizeof(__search) - 1 : 0, \
.match_len = (__match), \
}

#define SDP_HDR(__name, __search, __match) \
__SIP_HDR(__name, NULL, __search, __match)

enum sdp_header_types {
SDP_HDR_UNSPEC,
SDP_HDR_VERSION,
SDP_HDR_OWNER_IP4,
SDP_HDR_CONNECTION_IP4,
SDP_HDR_OWNER_IP6,
SDP_HDR_CONNECTION_IP6,
SDP_HDR_MEDIA,
};

extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb,
Expand All @@ -36,5 +66,12 @@ extern int ct_sip_lnlen(const char *line, const char *limit);
extern const char *ct_sip_search(const char *needle, const char *haystack,
size_t needle_len, size_t haystack_len,
int case_sensitive);

extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
unsigned int dataoff, unsigned int datalen,
enum sdp_header_types type,
enum sdp_header_types term,
unsigned int *matchoff, unsigned int *matchlen);

#endif /* __KERNEL__ */
#endif /* __NF_CONNTRACK_SIP_H__ */
69 changes: 32 additions & 37 deletions trunk/net/ipv4/netfilter/nf_nat_sip.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,51 +147,46 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
return NF_ACCEPT;
}

static unsigned int mangle_sip_packet(struct sk_buff *skb,
const char **dptr, unsigned int *datalen,
char *buffer, int bufflen,
enum sip_header_pos pos)
static int mangle_content_len(struct sk_buff *skb,
const char **dptr, unsigned int *datalen)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
unsigned int matchlen, matchoff;
unsigned int matchoff, matchlen;
char buffer[sizeof("65536")];
int buflen, c_len;

/* Get actual SDP length */
if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
SDP_HDR_VERSION, SDP_HDR_UNSPEC,
&matchoff, &matchlen) <= 0)
return 0;
c_len = *datalen - matchoff + strlen("v=");

/* Now, update SDP length */
if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen,
pos) <= 0)
POS_CONTENT) <= 0)
return 0;

buflen = sprintf(buffer, "%u", c_len);
return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
buffer, bufflen);
buffer, buflen);
}

static int mangle_content_len(struct sk_buff *skb,
const char **dptr, unsigned int *datalen)
static unsigned mangle_sdp_packet(struct sk_buff *skb,
const char **dptr, unsigned int *datalen,
enum sdp_header_types type,
char *buffer, int buflen)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
unsigned int matchoff, matchlen;
char buffer[sizeof("65536")];
int bufflen;
unsigned int matchlen, matchoff;

/* Get actual SDP length */
if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff,
&matchlen, POS_SDP_HEADER) > 0) {

/* since ct_sip_get_info() give us a pointer passing 'v='
we need to add 2 bytes in this count. */
int c_len = *datalen - matchoff + 2;

/* Now, update SDP length */
if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff,
&matchlen, POS_CONTENT) > 0) {

bufflen = sprintf(buffer, "%u", c_len);
return mangle_packet(skb, dptr, datalen,
matchoff, matchlen,
buffer, bufflen);
}
}
return 0;
if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, type, SDP_HDR_UNSPEC,
&matchoff, &matchlen) <= 0)
return 0;
return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
buffer, buflen);
}

static unsigned int mangle_sdp(struct sk_buff *skb,
Expand All @@ -205,18 +200,18 @@ static unsigned int mangle_sdp(struct sk_buff *skb,

/* Mangle owner and contact info. */
bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
POS_OWNER_IP4))
if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_OWNER_IP4,
buffer, bufflen))
return 0;

if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
POS_CONNECTION_IP4))
if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_CONNECTION_IP4,
buffer, bufflen))
return 0;

/* Mangle media port. */
bufflen = sprintf(buffer, "%u", port);
if (!mangle_sip_packet(skb, dptr, datalen, buffer, bufflen,
POS_MEDIA))
if (!mangle_sdp_packet(skb, dptr, datalen, SDP_HDR_MEDIA,
buffer, bufflen))
return 0;

return mangle_content_len(skb, dptr, datalen);
Expand Down
159 changes: 94 additions & 65 deletions trunk/net/netfilter/nf_conntrack_sip.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,66 +124,6 @@ static const struct sip_header_nfo ct_sip_hdrs[] = {
.ln_strlen = sizeof(":") - 1,
.match_len = skp_digits_len
},
[POS_MEDIA] = { /* SDP media info */
.case_sensitive = 1,
.lname = "\nm=",
.lnlen = sizeof("\nm=") - 1,
.sname = "\rm=",
.snlen = sizeof("\rm=") - 1,
.ln_str = "audio ",
.ln_strlen = sizeof("audio ") - 1,
.match_len = digits_len
},
[POS_OWNER_IP4] = { /* SDP owner address*/
.case_sensitive = 1,
.lname = "\no=",
.lnlen = sizeof("\no=") - 1,
.sname = "\ro=",
.snlen = sizeof("\ro=") - 1,
.ln_str = "IN IP4 ",
.ln_strlen = sizeof("IN IP4 ") - 1,
.match_len = epaddr_len
},
[POS_CONNECTION_IP4] = {/* SDP connection info */
.case_sensitive = 1,
.lname = "\nc=",
.lnlen = sizeof("\nc=") - 1,
.sname = "\rc=",
.snlen = sizeof("\rc=") - 1,
.ln_str = "IN IP4 ",
.ln_strlen = sizeof("IN IP4 ") - 1,
.match_len = epaddr_len
},
[POS_OWNER_IP6] = { /* SDP owner address*/
.case_sensitive = 1,
.lname = "\no=",
.lnlen = sizeof("\no=") - 1,
.sname = "\ro=",
.snlen = sizeof("\ro=") - 1,
.ln_str = "IN IP6 ",
.ln_strlen = sizeof("IN IP6 ") - 1,
.match_len = epaddr_len
},
[POS_CONNECTION_IP6] = {/* SDP connection info */
.case_sensitive = 1,
.lname = "\nc=",
.lnlen = sizeof("\nc=") - 1,
.sname = "\rc=",
.snlen = sizeof("\rc=") - 1,
.ln_str = "IN IP6 ",
.ln_strlen = sizeof("IN IP6 ") - 1,
.match_len = epaddr_len
},
[POS_SDP_HEADER] = { /* SDP version header */
.case_sensitive = 1,
.lname = "\nv=",
.lnlen = sizeof("\nv=") - 1,
.sname = "\rv=",
.snlen = sizeof("\rv=") - 1,
.ln_str = "=",
.ln_strlen = sizeof("=") - 1,
.match_len = digits_len
}
};

/* get line length until first CR or LF seen. */
Expand Down Expand Up @@ -363,6 +303,92 @@ int ct_sip_get_info(const struct nf_conn *ct,
}
EXPORT_SYMBOL_GPL(ct_sip_get_info);

/* SDP header parsing: a SDP session description contains an ordered set of
* headers, starting with a section containing general session parameters,
* optionally followed by multiple media descriptions.
*
* SDP headers always start at the beginning of a line. According to RFC 2327:
* "The sequence CRLF (0x0d0a) is used to end a record, although parsers should
* be tolerant and also accept records terminated with a single newline
* character". We handle both cases.
*/
static const struct sip_header ct_sdp_hdrs[] = {
[SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
[SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
[SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
[SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
[SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
[SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len),
};

/* Linear string search within SDP header values */
static const char *ct_sdp_header_search(const char *dptr, const char *limit,
const char *needle, unsigned int len)
{
for (limit -= len; dptr < limit; dptr++) {
if (*dptr == '\r' || *dptr == '\n')
break;
if (strncmp(dptr, needle, len) == 0)
return dptr;
}
return NULL;
}

/* Locate a SDP header (optionally a substring within the header value),
* optionally stopping at the first occurence of the term header, parse
* it and return the offset and length of the data we're interested in.
*/
int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
unsigned int dataoff, unsigned int datalen,
enum sdp_header_types type,
enum sdp_header_types term,
unsigned int *matchoff, unsigned int *matchlen)
{
const struct sip_header *hdr = &ct_sdp_hdrs[type];
const struct sip_header *thdr = &ct_sdp_hdrs[term];
const char *start = dptr, *limit = dptr + datalen;
int shift = 0;

for (dptr += dataoff; dptr < limit; dptr++) {
/* Find beginning of line */
if (*dptr != '\r' && *dptr != '\n')
continue;
if (++dptr >= limit)
break;
if (*(dptr - 1) == '\r' && *dptr == '\n') {
if (++dptr >= limit)
break;
}

if (term != SDP_HDR_UNSPEC &&
limit - dptr >= thdr->len &&
strnicmp(dptr, thdr->name, thdr->len) == 0)
break;
else if (limit - dptr >= hdr->len &&
strnicmp(dptr, hdr->name, hdr->len) == 0)
dptr += hdr->len;
else
continue;

*matchoff = dptr - start;
if (hdr->search) {
dptr = ct_sdp_header_search(dptr, limit, hdr->search,
hdr->slen);
if (!dptr)
return -1;
dptr += hdr->slen;
}

*matchlen = hdr->match_len(ct, dptr, limit, &shift);
if (!*matchlen)
return -1;
*matchoff = dptr - start + shift;
return 1;
}
return 0;
}
EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);

static int set_expected_rtp(struct sk_buff *skb,
const char **dptr, unsigned int *datalen,
union nf_inet_addr *addr, __be16 port)
Expand Down Expand Up @@ -408,7 +434,7 @@ static int sip_help(struct sk_buff *skb,
int ret = NF_ACCEPT;
unsigned int matchoff, matchlen;
u_int16_t port;
enum sip_header_pos pos;
enum sdp_header_types type;
typeof(nf_nat_sip_hook) nf_nat_sip;

/* No Data ? */
Expand Down Expand Up @@ -446,17 +472,20 @@ static int sip_help(struct sk_buff *skb,
goto out;
}
/* Get address and port from SDP packet. */
pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6;
if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) {
type = family == AF_INET ? SDP_HDR_CONNECTION_IP4 :
SDP_HDR_CONNECTION_IP6;
if (ct_sip_get_sdp_header(ct, dptr, 0, datalen, type, SDP_HDR_UNSPEC,
&matchoff, &matchlen) > 0) {

/* We'll drop only if there are parse problems. */
if (!parse_addr(ct, dptr + matchoff, NULL, &addr,
dptr + datalen)) {
ret = NF_DROP;
goto out;
}
if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen,
POS_MEDIA) > 0) {
if (ct_sip_get_sdp_header(ct, dptr, 0, datalen,
SDP_HDR_MEDIA, SDP_HDR_UNSPEC,
&matchoff, &matchlen) > 0) {

port = simple_strtoul(dptr + matchoff, NULL, 10);
if (port < 1024) {
Expand Down

0 comments on commit 107f20f

Please sign in to comment.