Skip to content

Commit 1b683b5

Browse files
kaberDavid S. Miller
authored andcommitted
[NETFILTER]: sip conntrack: better NAT handling
The NAT handling of the SIP helper has a few problems: - Request headers are only mangled in the reply direction, From/To headers not at all, which can lead to authentication failures with DNAT in case the authentication domain is the IP address - Contact headers in responses are only mangled for REGISTER responses - Headers may be mangled even though they contain addresses not participating in the connection, like alternative addresses - Packets are droppen when domain names are used where the helper expects IP addresses This patch takes a different approach, instead of fixed rules what field to mangle to what content, it adds symetric mapping of From/To/Via/Contact headers, which allows to deal properly with echoed addresses in responses and foreign addresses not belonging to the connection. Signed-off-by: Patrick McHardy <kaber@trash.net>
1 parent 77a78de commit 1b683b5

File tree

3 files changed

+135
-70
lines changed

3 files changed

+135
-70
lines changed

include/linux/netfilter_ipv4/ip_conntrack_sip.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
#define SIP_TIMEOUT 3600
77

88
enum sip_header_pos {
9-
POS_REQ_HEADER,
9+
POS_REG_REQ_URI,
10+
POS_REQ_URI,
11+
POS_FROM,
12+
POS_TO,
1013
POS_VIA,
1114
POS_CONTACT,
1215
POS_CONTENT,

net/ipv4/netfilter/ip_conntrack_sip.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,38 @@ struct sip_header_nfo {
6969
};
7070

7171
static struct sip_header_nfo ct_sip_hdrs[] = {
72-
[POS_REQ_HEADER] = { /* SIP Requests headers */
72+
[POS_REG_REQ_URI] = { /* SIP REGISTER request URI */
73+
.lname = "sip:",
74+
.lnlen = sizeof("sip:") - 1,
75+
.ln_str = ":",
76+
.ln_strlen = sizeof(":") - 1,
77+
.match_len = epaddr_len
78+
},
79+
[POS_REQ_URI] = { /* SIP request URI */
7380
.lname = "sip:",
7481
.lnlen = sizeof("sip:") - 1,
7582
.ln_str = "@",
7683
.ln_strlen = sizeof("@") - 1,
7784
.match_len = epaddr_len
7885
},
86+
[POS_FROM] = { /* SIP From header */
87+
.lname = "From:",
88+
.lnlen = sizeof("From:") - 1,
89+
.sname = "\r\nf:",
90+
.snlen = sizeof("\r\nf:") - 1,
91+
.ln_str = "sip:",
92+
.ln_strlen = sizeof("sip:") - 1,
93+
.match_len = skp_epaddr_len,
94+
},
95+
[POS_TO] = { /* SIP To header */
96+
.lname = "To:",
97+
.lnlen = sizeof("To:") - 1,
98+
.sname = "\r\nt:",
99+
.snlen = sizeof("\r\nt:") - 1,
100+
.ln_str = "sip:",
101+
.ln_strlen = sizeof("sip:") - 1,
102+
.match_len = skp_epaddr_len,
103+
},
79104
[POS_VIA] = { /* SIP Via header */
80105
.lname = "Via:",
81106
.lnlen = sizeof("Via:") - 1,
@@ -284,7 +309,7 @@ int ct_sip_get_info(const char *dptr, size_t dlen,
284309

285310
while (dptr <= limit) {
286311
if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
287-
(hinfo->sname == NULL ||
312+
(hnfo->sname == NULL ||
288313
strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
289314
dptr++;
290315
continue;

net/ipv4/netfilter/ip_nat_sip.c

Lines changed: 104 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -29,95 +29,132 @@ MODULE_DESCRIPTION("SIP NAT helper");
2929
#define DEBUGP(format, args...)
3030
#endif
3131

32-
static unsigned int mangle_sip_packet(struct sk_buff **pskb,
33-
enum ip_conntrack_info ctinfo,
34-
struct ip_conntrack *ct,
35-
const char **dptr, size_t dlen,
36-
char *buffer, int bufflen,
37-
enum sip_header_pos pos)
32+
struct addr_map {
33+
struct {
34+
char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
35+
char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
36+
unsigned int srclen, srciplen;
37+
unsigned int dstlen, dstiplen;
38+
} addr[IP_CT_DIR_MAX];
39+
};
40+
41+
static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map)
3842
{
39-
unsigned int matchlen, matchoff;
43+
struct ip_conntrack_tuple *t;
44+
enum ip_conntrack_dir dir;
45+
unsigned int n;
46+
47+
for (dir = 0; dir < IP_CT_DIR_MAX; dir++) {
48+
t = &ct->tuplehash[dir].tuple;
49+
50+
n = sprintf(map->addr[dir].src, "%u.%u.%u.%u",
51+
NIPQUAD(t->src.ip));
52+
map->addr[dir].srciplen = n;
53+
n += sprintf(map->addr[dir].src + n, ":%u",
54+
ntohs(t->src.u.udp.port));
55+
map->addr[dir].srclen = n;
56+
57+
n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u",
58+
NIPQUAD(t->dst.ip));
59+
map->addr[dir].dstiplen = n;
60+
n += sprintf(map->addr[dir].dst + n, ":%u",
61+
ntohs(t->dst.u.udp.port));
62+
map->addr[dir].dstlen = n;
63+
}
64+
}
65+
66+
static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo,
67+
struct ip_conntrack *ct, const char **dptr, size_t dlen,
68+
enum sip_header_pos pos, struct addr_map *map)
69+
{
70+
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
71+
unsigned int matchlen, matchoff, addrlen;
72+
char *addr;
4073

4174
if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
42-
return 0;
75+
return 1;
76+
77+
if ((matchlen == map->addr[dir].srciplen ||
78+
matchlen == map->addr[dir].srclen) &&
79+
memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) {
80+
addr = map->addr[!dir].dst;
81+
addrlen = map->addr[!dir].dstlen;
82+
} else if ((matchlen == map->addr[dir].dstiplen ||
83+
matchlen == map->addr[dir].dstlen) &&
84+
memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) {
85+
addr = map->addr[!dir].src;
86+
addrlen = map->addr[!dir].srclen;
87+
} else
88+
return 1;
4389

4490
if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
45-
matchoff, matchlen, buffer, bufflen))
91+
matchoff, matchlen, addr, addrlen))
4692
return 0;
47-
48-
/* We need to reload this. Thanks Patrick. */
4993
*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
5094
return 1;
95+
5196
}
5297

5398
static unsigned int ip_nat_sip(struct sk_buff **pskb,
5499
enum ip_conntrack_info ctinfo,
55100
struct ip_conntrack *ct,
56101
const char **dptr)
57102
{
58-
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
59-
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
60-
unsigned int bufflen, dataoff;
61-
__be32 ip;
62-
__be16 port;
103+
enum sip_header_pos pos;
104+
struct addr_map map;
105+
int dataoff, datalen;
63106

64107
dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
108+
datalen = (*pskb)->len - dataoff;
109+
if (datalen < sizeof("SIP/2.0") - 1)
110+
return NF_DROP;
111+
112+
addr_map_init(ct, &map);
113+
114+
/* Basic rules: requests and responses. */
115+
if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) {
116+
/* 10.2: Constructing the REGISTER Request:
117+
*
118+
* The "userinfo" and "@" components of the SIP URI MUST NOT
119+
* be present.
120+
*/
121+
if (datalen >= sizeof("REGISTER") - 1 &&
122+
strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0)
123+
pos = POS_REG_REQ_URI;
124+
else
125+
pos = POS_REQ_URI;
126+
127+
if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map))
128+
return NF_DROP;
129+
}
65130

66-
ip = ct->tuplehash[!dir].tuple.dst.ip;
67-
port = ct->tuplehash[!dir].tuple.dst.u.udp.port;
68-
bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port));
131+
if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) ||
132+
!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) ||
133+
!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) ||
134+
!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map))
135+
return NF_DROP;
136+
return NF_ACCEPT;
137+
}
138+
139+
static unsigned int mangle_sip_packet(struct sk_buff **pskb,
140+
enum ip_conntrack_info ctinfo,
141+
struct ip_conntrack *ct,
142+
const char **dptr, size_t dlen,
143+
char *buffer, int bufflen,
144+
enum sip_header_pos pos)
145+
{
146+
unsigned int matchlen, matchoff;
69147

70-
/* short packet ? */
71-
if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
148+
if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
72149
return 0;
73150

74-
/* Basic rules: requests and responses. */
75-
if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
76-
const char *aux;
77-
78-
if ((ctinfo) < IP_CT_IS_REPLY) {
79-
mangle_sip_packet(pskb, ctinfo, ct, dptr,
80-
(*pskb)->len - dataoff,
81-
buffer, bufflen, POS_CONTACT);
82-
return 1;
83-
}
151+
if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
152+
matchoff, matchlen, buffer, bufflen))
153+
return 0;
84154

85-
if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
86-
(*pskb)->len - dataoff,
87-
buffer, bufflen, POS_VIA))
88-
return 0;
89-
90-
aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
91-
(*pskb)->len - dataoff, 0);
92-
if (!aux)
93-
return 0;
94-
95-
if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
96-
ct_sip_lnlen(aux,
97-
*dptr + (*pskb)->len - dataoff),
98-
1))
99-
return 1;
100-
101-
return mangle_sip_packet(pskb, ctinfo, ct, dptr,
102-
(*pskb)->len - dataoff,
103-
buffer, bufflen, POS_CONTACT);
104-
}
105-
if ((ctinfo) < IP_CT_IS_REPLY) {
106-
if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
107-
(*pskb)->len - dataoff,
108-
buffer, bufflen, POS_VIA))
109-
return 0;
110-
111-
/* Mangle Contact if exists only. - watch udp_nat_mangle()! */
112-
mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,
113-
buffer, bufflen, POS_CONTACT);
114-
return 1;
115-
}
116-
/* This mangle requests headers. */
117-
return mangle_sip_packet(pskb, ctinfo, ct, dptr,
118-
ct_sip_lnlen(*dptr,
119-
*dptr + (*pskb)->len - dataoff),
120-
buffer, bufflen, POS_REQ_HEADER);
155+
/* We need to reload this. Thanks Patrick. */
156+
*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
157+
return 1;
121158
}
122159

123160
static int mangle_content_len(struct sk_buff **pskb,

0 commit comments

Comments
 (0)