1/* SIP extension for UDP NAT alteration. 2 * 3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> 4 * based on RR's ip_nat_ftp.c and other modules. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/module.h> 12#include <linux/skbuff.h> 13#include <linux/ip.h> 14#include <net/ip.h> 15#include <linux/udp.h> 16 17#include <net/netfilter/nf_nat.h> 18#include <net/netfilter/nf_nat_helper.h> 19#include <net/netfilter/nf_nat_rule.h> 20#include <net/netfilter/nf_conntrack_helper.h> 21#include <net/netfilter/nf_conntrack_expect.h> 22#include <linux/netfilter/nf_conntrack_sip.h> 23 24MODULE_LICENSE("GPL"); 25MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); 26MODULE_DESCRIPTION("SIP NAT helper"); 27MODULE_ALIAS("ip_nat_sip"); 28 29#define DEBUGP(format, args...) 30 31struct addr_map { 32 struct { 33 char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; 34 char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; 35 unsigned int srclen, srciplen; 36 unsigned int dstlen, dstiplen; 37 } addr[IP_CT_DIR_MAX]; 38}; 39 40static void addr_map_init(struct nf_conn *ct, struct addr_map *map) 41{ 42 struct nf_conntrack_tuple *t; 43 enum ip_conntrack_dir dir; 44 unsigned int n; 45 46 for (dir = 0; dir < IP_CT_DIR_MAX; dir++) { 47 t = &ct->tuplehash[dir].tuple; 48 49 n = sprintf(map->addr[dir].src, "%u.%u.%u.%u", 50 NIPQUAD(t->src.u3.ip)); 51 map->addr[dir].srciplen = n; 52 n += sprintf(map->addr[dir].src + n, ":%u", 53 ntohs(t->src.u.udp.port)); 54 map->addr[dir].srclen = n; 55 56 n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u", 57 NIPQUAD(t->dst.u3.ip)); 58 map->addr[dir].dstiplen = n; 59 n += sprintf(map->addr[dir].dst + n, ":%u", 60 ntohs(t->dst.u.udp.port)); 61 map->addr[dir].dstlen = n; 62 } 63} 64 65static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, 66 struct nf_conn *ct, const char **dptr, size_t dlen, 67 enum sip_header_pos pos, struct addr_map *map) 68{ 69 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 70 unsigned int matchlen, matchoff, addrlen; 71 char *addr; 72 73 if (ct_sip_get_info(ct, *dptr, dlen, &matchoff, &matchlen, pos) <= 0) 74 return 1; 75 76 if ((matchlen == map->addr[dir].srciplen || 77 matchlen == map->addr[dir].srclen) && 78 memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) { 79 addr = map->addr[!dir].dst; 80 addrlen = map->addr[!dir].dstlen; 81 } else if ((matchlen == map->addr[dir].dstiplen || 82 matchlen == map->addr[dir].dstlen) && 83 memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) { 84 addr = map->addr[!dir].src; 85 addrlen = map->addr[!dir].srclen; 86 } else 87 return 1; 88 89 if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, 90 matchoff, matchlen, addr, addrlen)) 91 return 0; 92 *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); 93 return 1; 94 95} 96 97static unsigned int ip_nat_sip(struct sk_buff **pskb, 98 enum ip_conntrack_info ctinfo, 99 struct nf_conn *ct, 100 const char **dptr) 101{ 102 enum sip_header_pos pos; 103 struct addr_map map; 104 int dataoff, datalen; 105 106 dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); 107 datalen = (*pskb)->len - dataoff; 108 if (datalen < sizeof("SIP/2.0") - 1) 109 return NF_DROP; 110 111 addr_map_init(ct, &map); 112 113 /* Basic rules: requests and responses. */ 114 if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) { 115 /* 10.2: Constructing the REGISTER Request: 116 * 117 * The "userinfo" and "@" components of the SIP URI MUST NOT 118 * be present. 119 */ 120 if (datalen >= sizeof("REGISTER") - 1 && 121 strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0) 122 pos = POS_REG_REQ_URI; 123 else 124 pos = POS_REQ_URI; 125 126 if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map)) 127 return NF_DROP; 128 } 129 130 if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) || 131 !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) || 132 !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) || 133 !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map)) 134 return NF_DROP; 135 return NF_ACCEPT; 136} 137 138static unsigned int mangle_sip_packet(struct sk_buff **pskb, 139 enum ip_conntrack_info ctinfo, 140 struct nf_conn *ct, 141 const char **dptr, size_t dlen, 142 char *buffer, int bufflen, 143 enum sip_header_pos pos) 144{ 145 unsigned int matchlen, matchoff; 146 147 if (ct_sip_get_info(ct, *dptr, dlen, &matchoff, &matchlen, pos) <= 0) 148 return 0; 149 150 if (!nf_nat_mangle_udp_packet(pskb, ct, ctinfo, 151 matchoff, matchlen, buffer, bufflen)) 152 return 0; 153 154 /* We need to reload this. Thanks Patrick. */ 155 *dptr = (*pskb)->data + ip_hdrlen(*pskb) + sizeof(struct udphdr); 156 return 1; 157} 158 159static int mangle_content_len(struct sk_buff **pskb, 160 enum ip_conntrack_info ctinfo, 161 struct nf_conn *ct, 162 const char *dptr) 163{ 164 unsigned int dataoff, matchoff, matchlen; 165 char buffer[sizeof("65536")]; 166 int bufflen; 167 168 dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); 169 170 /* Get actual SDP lenght */ 171 if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff, 172 &matchlen, POS_SDP_HEADER) > 0) { 173 174 /* since ct_sip_get_info() give us a pointer passing 'v=' 175 we need to add 2 bytes in this count. */ 176 int c_len = (*pskb)->len - dataoff - matchoff + 2; 177 178 /* Now, update SDP length */ 179 if (ct_sip_get_info(ct, dptr, (*pskb)->len - dataoff, &matchoff, 180 &matchlen, POS_CONTENT) > 0) { 181 182 bufflen = sprintf(buffer, "%u", c_len); 183 return nf_nat_mangle_udp_packet(pskb, ct, ctinfo, 184 matchoff, matchlen, 185 buffer, bufflen); 186 } 187 } 188 return 0; 189} 190 191static unsigned int mangle_sdp(struct sk_buff **pskb, 192 enum ip_conntrack_info ctinfo, 193 struct nf_conn *ct, 194 __be32 newip, u_int16_t port, 195 const char *dptr) 196{ 197 char buffer[sizeof("nnn.nnn.nnn.nnn")]; 198 unsigned int dataoff, bufflen; 199 200 dataoff = ip_hdrlen(*pskb) + sizeof(struct udphdr); 201 202 /* Mangle owner and contact info. */ 203 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); 204 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, 205 buffer, bufflen, POS_OWNER_IP4)) 206 return 0; 207 208 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, 209 buffer, bufflen, POS_CONNECTION_IP4)) 210 return 0; 211 212 /* Mangle media port. */ 213 bufflen = sprintf(buffer, "%u", port); 214 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, 215 buffer, bufflen, POS_MEDIA)) 216 return 0; 217 218 return mangle_content_len(pskb, ctinfo, ct, dptr); 219} 220 221static void ip_nat_sdp_expect(struct nf_conn *ct, 222 struct nf_conntrack_expect *exp) 223{ 224 struct nf_nat_range range; 225 226 /* This must be a fresh one. */ 227 BUG_ON(ct->status & IPS_NAT_DONE_MASK); 228 229 /* Change src to where master sends to */ 230 range.flags = IP_NAT_RANGE_MAP_IPS; 231 range.min_ip = range.max_ip 232 = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; 233 /* hook doesn't matter, but it has to do source manip */ 234 nf_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); 235 236 /* For DST manip, map port here to where it's expected. */ 237 range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); 238 range.min = range.max = exp->saved_proto; 239 range.min_ip = range.max_ip = exp->saved_ip; 240 /* hook doesn't matter, but it has to do destination manip */ 241 nf_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); 242} 243 244/* So, this packet has hit the connection tracking matching code. 245 Mangle it, and change the expectation to match the new version. */ 246static unsigned int ip_nat_sdp(struct sk_buff **pskb, 247 enum ip_conntrack_info ctinfo, 248 struct nf_conntrack_expect *exp, 249 const char *dptr) 250{ 251 struct nf_conn *ct = exp->master; 252 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 253 __be32 newip; 254 u_int16_t port; 255 256 DEBUGP("ip_nat_sdp():\n"); 257 258 /* Connection will come from reply */ 259 newip = ct->tuplehash[!dir].tuple.dst.u3.ip; 260 261 exp->saved_ip = exp->tuple.dst.u3.ip; 262 exp->tuple.dst.u3.ip = newip; 263 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; 264 exp->dir = !dir; 265 266 /* When you see the packet, we need to NAT it the same as the 267 this one. */ 268 exp->expectfn = ip_nat_sdp_expect; 269 270 /* Try to get same port: if not, try to change it. */ 271 for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { 272 exp->tuple.dst.u.udp.port = htons(port); 273 if (nf_conntrack_expect_related(exp) == 0) 274 break; 275 } 276 277 if (port == 0) 278 return NF_DROP; 279 280 if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { 281 nf_conntrack_unexpect_related(exp); 282 return NF_DROP; 283 } 284 return NF_ACCEPT; 285} 286 287static void __exit nf_nat_sip_fini(void) 288{ 289 rcu_assign_pointer(nf_nat_sip_hook, NULL); 290 rcu_assign_pointer(nf_nat_sdp_hook, NULL); 291 synchronize_rcu(); 292} 293 294static int __init nf_nat_sip_init(void) 295{ 296 BUG_ON(rcu_dereference(nf_nat_sip_hook)); 297 BUG_ON(rcu_dereference(nf_nat_sdp_hook)); 298 rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); 299 rcu_assign_pointer(nf_nat_sdp_hook, ip_nat_sdp); 300 return 0; 301} 302 303module_init(nf_nat_sip_init); 304module_exit(nf_nat_sip_fini); 305