linux/net/netfilter/nf_nat_sip.c
<<
>>
Prefs
   1/* SIP extension for 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 * (C) 2007 United Security Providers
   6 * (C) 2007, 2008, 2011, 2012 Patrick McHardy <kaber@trash.net>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/skbuff.h>
  15#include <linux/inet.h>
  16#include <linux/udp.h>
  17#include <linux/tcp.h>
  18
  19#include <net/netfilter/nf_nat.h>
  20#include <net/netfilter/nf_nat_helper.h>
  21#include <net/netfilter/nf_conntrack_helper.h>
  22#include <net/netfilter/nf_conntrack_expect.h>
  23#include <linux/netfilter/nf_conntrack_sip.h>
  24
  25MODULE_LICENSE("GPL");
  26MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
  27MODULE_DESCRIPTION("SIP NAT helper");
  28MODULE_ALIAS("ip_nat_sip");
  29
  30
  31static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff,
  32                                  unsigned int dataoff,
  33                                  const char **dptr, unsigned int *datalen,
  34                                  unsigned int matchoff, unsigned int matchlen,
  35                                  const char *buffer, unsigned int buflen)
  36{
  37        enum ip_conntrack_info ctinfo;
  38        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
  39        struct tcphdr *th;
  40        unsigned int baseoff;
  41
  42        if (nf_ct_protonum(ct) == IPPROTO_TCP) {
  43                th = (struct tcphdr *)(skb->data + protoff);
  44                baseoff = protoff + th->doff * 4;
  45                matchoff += dataoff - baseoff;
  46
  47                if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
  48                                                protoff, matchoff, matchlen,
  49                                                buffer, buflen, false))
  50                        return 0;
  51        } else {
  52                baseoff = protoff + sizeof(struct udphdr);
  53                matchoff += dataoff - baseoff;
  54
  55                if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
  56                                              protoff, matchoff, matchlen,
  57                                              buffer, buflen))
  58                        return 0;
  59        }
  60
  61        /* Reload data pointer and adjust datalen value */
  62        *dptr = skb->data + dataoff;
  63        *datalen += buflen - matchlen;
  64        return 1;
  65}
  66
  67static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer,
  68                            const union nf_inet_addr *addr, bool delim)
  69{
  70        if (nf_ct_l3num(ct) == NFPROTO_IPV4)
  71                return sprintf(buffer, "%pI4", &addr->ip);
  72        else {
  73                if (delim)
  74                        return sprintf(buffer, "[%pI6c]", &addr->ip6);
  75                else
  76                        return sprintf(buffer, "%pI6c", &addr->ip6);
  77        }
  78}
  79
  80static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer,
  81                                 const union nf_inet_addr *addr, u16 port)
  82{
  83        if (nf_ct_l3num(ct) == NFPROTO_IPV4)
  84                return sprintf(buffer, "%pI4:%u", &addr->ip, port);
  85        else
  86                return sprintf(buffer, "[%pI6c]:%u", &addr->ip6, port);
  87}
  88
  89static int map_addr(struct sk_buff *skb, unsigned int protoff,
  90                    unsigned int dataoff,
  91                    const char **dptr, unsigned int *datalen,
  92                    unsigned int matchoff, unsigned int matchlen,
  93                    union nf_inet_addr *addr, __be16 port)
  94{
  95        enum ip_conntrack_info ctinfo;
  96        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
  97        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  98        char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
  99        unsigned int buflen;
 100        union nf_inet_addr newaddr;
 101        __be16 newport;
 102
 103        if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) &&
 104            ct->tuplehash[dir].tuple.src.u.udp.port == port) {
 105                newaddr = ct->tuplehash[!dir].tuple.dst.u3;
 106                newport = ct->tuplehash[!dir].tuple.dst.u.udp.port;
 107        } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) &&
 108                   ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
 109                newaddr = ct->tuplehash[!dir].tuple.src.u3;
 110                newport = ct->tuplehash[!dir].tuple.src.u.udp.port;
 111        } else
 112                return 1;
 113
 114        if (nf_inet_addr_cmp(&newaddr, addr) && newport == port)
 115                return 1;
 116
 117        buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport));
 118        return mangle_packet(skb, protoff, dataoff, dptr, datalen,
 119                             matchoff, matchlen, buffer, buflen);
 120}
 121
 122static int map_sip_addr(struct sk_buff *skb, unsigned int protoff,
 123                        unsigned int dataoff,
 124                        const char **dptr, unsigned int *datalen,
 125                        enum sip_header_types type)
 126{
 127        enum ip_conntrack_info ctinfo;
 128        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 129        unsigned int matchlen, matchoff;
 130        union nf_inet_addr addr;
 131        __be16 port;
 132
 133        if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, type, NULL,
 134                                    &matchoff, &matchlen, &addr, &port) <= 0)
 135                return 1;
 136        return map_addr(skb, protoff, dataoff, dptr, datalen,
 137                        matchoff, matchlen, &addr, port);
 138}
 139
 140static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
 141                               unsigned int dataoff,
 142                               const char **dptr, unsigned int *datalen)
 143{
 144        enum ip_conntrack_info ctinfo;
 145        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 146        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 147        unsigned int coff, matchoff, matchlen;
 148        enum sip_header_types hdr;
 149        union nf_inet_addr addr;
 150        __be16 port;
 151        int request, in_header;
 152
 153        /* Basic rules: requests and responses. */
 154        if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
 155                if (ct_sip_parse_request(ct, *dptr, *datalen,
 156                                         &matchoff, &matchlen,
 157                                         &addr, &port) > 0 &&
 158                    !map_addr(skb, protoff, dataoff, dptr, datalen,
 159                              matchoff, matchlen, &addr, port))
 160                        return NF_DROP;
 161                request = 1;
 162        } else
 163                request = 0;
 164
 165        if (nf_ct_protonum(ct) == IPPROTO_TCP)
 166                hdr = SIP_HDR_VIA_TCP;
 167        else
 168                hdr = SIP_HDR_VIA_UDP;
 169
 170        /* Translate topmost Via header and parameters */
 171        if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
 172                                    hdr, NULL, &matchoff, &matchlen,
 173                                    &addr, &port) > 0) {
 174                unsigned int olen, matchend, poff, plen, buflen, n;
 175                char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
 176
 177                /* We're only interested in headers related to this
 178                 * connection */
 179                if (request) {
 180                        if (!nf_inet_addr_cmp(&addr,
 181                                        &ct->tuplehash[dir].tuple.src.u3) ||
 182                            port != ct->tuplehash[dir].tuple.src.u.udp.port)
 183                                goto next;
 184                } else {
 185                        if (!nf_inet_addr_cmp(&addr,
 186                                        &ct->tuplehash[dir].tuple.dst.u3) ||
 187                            port != ct->tuplehash[dir].tuple.dst.u.udp.port)
 188                                goto next;
 189                }
 190
 191                olen = *datalen;
 192                if (!map_addr(skb, protoff, dataoff, dptr, datalen,
 193                              matchoff, matchlen, &addr, port))
 194                        return NF_DROP;
 195
 196                matchend = matchoff + matchlen + *datalen - olen;
 197
 198                /* The maddr= parameter (RFC 2361) specifies where to send
 199                 * the reply. */
 200                if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
 201                                               "maddr=", &poff, &plen,
 202                                               &addr, true) > 0 &&
 203                    nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3) &&
 204                    !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3)) {
 205                        buflen = sip_sprintf_addr(ct, buffer,
 206                                        &ct->tuplehash[!dir].tuple.dst.u3,
 207                                        true);
 208                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
 209                                           poff, plen, buffer, buflen))
 210                                return NF_DROP;
 211                }
 212
 213                /* The received= parameter (RFC 2361) contains the address
 214                 * from which the server received the request. */
 215                if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
 216                                               "received=", &poff, &plen,
 217                                               &addr, false) > 0 &&
 218                    nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.dst.u3) &&
 219                    !nf_inet_addr_cmp(&addr, &ct->tuplehash[!dir].tuple.src.u3)) {
 220                        buflen = sip_sprintf_addr(ct, buffer,
 221                                        &ct->tuplehash[!dir].tuple.src.u3,
 222                                        false);
 223                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
 224                                           poff, plen, buffer, buflen))
 225                                return NF_DROP;
 226                }
 227
 228                /* The rport= parameter (RFC 3581) contains the port number
 229                 * from which the server received the request. */
 230                if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
 231                                                 "rport=", &poff, &plen,
 232                                                 &n) > 0 &&
 233                    htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
 234                    htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
 235                        __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
 236                        buflen = sprintf(buffer, "%u", ntohs(p));
 237                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
 238                                           poff, plen, buffer, buflen))
 239                                return NF_DROP;
 240                }
 241        }
 242
 243next:
 244        /* Translate Contact headers */
 245        coff = 0;
 246        in_header = 0;
 247        while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
 248                                       SIP_HDR_CONTACT, &in_header,
 249                                       &matchoff, &matchlen,
 250                                       &addr, &port) > 0) {
 251                if (!map_addr(skb, protoff, dataoff, dptr, datalen,
 252                              matchoff, matchlen,
 253                              &addr, port))
 254                        return NF_DROP;
 255        }
 256
 257        if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||
 258            !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
 259                return NF_DROP;
 260
 261        return NF_ACCEPT;
 262}
 263
 264static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff,
 265                                  s16 off)
 266{
 267        enum ip_conntrack_info ctinfo;
 268        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 269        const struct tcphdr *th;
 270
 271        if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0)
 272                return;
 273
 274        th = (struct tcphdr *)(skb->data + protoff);
 275        nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
 276}
 277
 278/* Handles expected signalling connections and media streams */
 279static void nf_nat_sip_expected(struct nf_conn *ct,
 280                                struct nf_conntrack_expect *exp)
 281{
 282        struct nf_nat_range range;
 283
 284        /* This must be a fresh one. */
 285        BUG_ON(ct->status & IPS_NAT_DONE_MASK);
 286
 287        /* For DST manip, map port here to where it's expected. */
 288        range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
 289        range.min_proto = range.max_proto = exp->saved_proto;
 290        range.min_addr = range.max_addr = exp->saved_addr;
 291        nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
 292
 293        /* Change src to where master sends to, but only if the connection
 294         * actually came from the same source. */
 295        if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
 296                             &ct->master->tuplehash[exp->dir].tuple.src.u3)) {
 297                range.flags = NF_NAT_RANGE_MAP_IPS;
 298                range.min_addr = range.max_addr
 299                        = ct->master->tuplehash[!exp->dir].tuple.dst.u3;
 300                nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
 301        }
 302}
 303
 304static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
 305                                      unsigned int dataoff,
 306                                      const char **dptr, unsigned int *datalen,
 307                                      struct nf_conntrack_expect *exp,
 308                                      unsigned int matchoff,
 309                                      unsigned int matchlen)
 310{
 311        enum ip_conntrack_info ctinfo;
 312        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 313        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 314        union nf_inet_addr newaddr;
 315        u_int16_t port;
 316        char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
 317        unsigned int buflen;
 318
 319        /* Connection will come from reply */
 320        if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
 321                             &ct->tuplehash[!dir].tuple.dst.u3))
 322                newaddr = exp->tuple.dst.u3;
 323        else
 324                newaddr = ct->tuplehash[!dir].tuple.dst.u3;
 325
 326        /* If the signalling port matches the connection's source port in the
 327         * original direction, try to use the destination port in the opposite
 328         * direction. */
 329        if (exp->tuple.dst.u.udp.port ==
 330            ct->tuplehash[dir].tuple.src.u.udp.port)
 331                port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
 332        else
 333                port = ntohs(exp->tuple.dst.u.udp.port);
 334
 335        exp->saved_addr = exp->tuple.dst.u3;
 336        exp->tuple.dst.u3 = newaddr;
 337        exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
 338        exp->dir = !dir;
 339        exp->expectfn = nf_nat_sip_expected;
 340
 341        for (; port != 0; port++) {
 342                int ret;
 343
 344                exp->tuple.dst.u.udp.port = htons(port);
 345                ret = nf_ct_expect_related(exp);
 346                if (ret == 0)
 347                        break;
 348                else if (ret != -EBUSY) {
 349                        port = 0;
 350                        break;
 351                }
 352        }
 353
 354        if (port == 0)
 355                return NF_DROP;
 356
 357        if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) ||
 358            exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
 359                buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port);
 360                if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
 361                                   matchoff, matchlen, buffer, buflen))
 362                        goto err;
 363        }
 364        return NF_ACCEPT;
 365
 366err:
 367        nf_ct_unexpect_related(exp);
 368        return NF_DROP;
 369}
 370
 371static int mangle_content_len(struct sk_buff *skb, unsigned int protoff,
 372                              unsigned int dataoff,
 373                              const char **dptr, unsigned int *datalen)
 374{
 375        enum ip_conntrack_info ctinfo;
 376        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 377        unsigned int matchoff, matchlen;
 378        char buffer[sizeof("65536")];
 379        int buflen, c_len;
 380
 381        /* Get actual SDP length */
 382        if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen,
 383                                  SDP_HDR_VERSION, SDP_HDR_UNSPEC,
 384                                  &matchoff, &matchlen) <= 0)
 385                return 0;
 386        c_len = *datalen - matchoff + strlen("v=");
 387
 388        /* Now, update SDP length */
 389        if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
 390                              &matchoff, &matchlen) <= 0)
 391                return 0;
 392
 393        buflen = sprintf(buffer, "%u", c_len);
 394        return mangle_packet(skb, protoff, dataoff, dptr, datalen,
 395                             matchoff, matchlen, buffer, buflen);
 396}
 397
 398static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff,
 399                             unsigned int dataoff,
 400                             const char **dptr, unsigned int *datalen,
 401                             unsigned int sdpoff,
 402                             enum sdp_header_types type,
 403                             enum sdp_header_types term,
 404                             char *buffer, int buflen)
 405{
 406        enum ip_conntrack_info ctinfo;
 407        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 408        unsigned int matchlen, matchoff;
 409
 410        if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, type, term,
 411                                  &matchoff, &matchlen) <= 0)
 412                return -ENOENT;
 413        return mangle_packet(skb, protoff, dataoff, dptr, datalen,
 414                             matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL;
 415}
 416
 417static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff,
 418                                    unsigned int dataoff,
 419                                    const char **dptr, unsigned int *datalen,
 420                                    unsigned int sdpoff,
 421                                    enum sdp_header_types type,
 422                                    enum sdp_header_types term,
 423                                    const union nf_inet_addr *addr)
 424{
 425        enum ip_conntrack_info ctinfo;
 426        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 427        char buffer[INET6_ADDRSTRLEN];
 428        unsigned int buflen;
 429
 430        buflen = sip_sprintf_addr(ct, buffer, addr, false);
 431        if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen,
 432                              sdpoff, type, term, buffer, buflen))
 433                return 0;
 434
 435        return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
 436}
 437
 438static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff,
 439                                    unsigned int dataoff,
 440                                    const char **dptr, unsigned int *datalen,
 441                                    unsigned int matchoff,
 442                                    unsigned int matchlen,
 443                                    u_int16_t port)
 444{
 445        char buffer[sizeof("nnnnn")];
 446        unsigned int buflen;
 447
 448        buflen = sprintf(buffer, "%u", port);
 449        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
 450                           matchoff, matchlen, buffer, buflen))
 451                return 0;
 452
 453        return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
 454}
 455
 456static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff,
 457                                       unsigned int dataoff,
 458                                       const char **dptr, unsigned int *datalen,
 459                                       unsigned int sdpoff,
 460                                       const union nf_inet_addr *addr)
 461{
 462        enum ip_conntrack_info ctinfo;
 463        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 464        char buffer[INET6_ADDRSTRLEN];
 465        unsigned int buflen;
 466
 467        /* Mangle session description owner and contact addresses */
 468        buflen = sip_sprintf_addr(ct, buffer, addr, false);
 469        if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
 470                              SDP_HDR_OWNER, SDP_HDR_MEDIA, buffer, buflen))
 471                return 0;
 472
 473        switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff,
 474                                  SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
 475                                  buffer, buflen)) {
 476        case 0:
 477        /*
 478         * RFC 2327:
 479         *
 480         * Session description
 481         *
 482         * c=* (connection information - not required if included in all media)
 483         */
 484        case -ENOENT:
 485                break;
 486        default:
 487                return 0;
 488        }
 489
 490        return mangle_content_len(skb, protoff, dataoff, dptr, datalen);
 491}
 492
 493/* So, this packet has hit the connection tracking matching code.
 494   Mangle it, and change the expectation to match the new version. */
 495static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
 496                                     unsigned int dataoff,
 497                                     const char **dptr, unsigned int *datalen,
 498                                     struct nf_conntrack_expect *rtp_exp,
 499                                     struct nf_conntrack_expect *rtcp_exp,
 500                                     unsigned int mediaoff,
 501                                     unsigned int medialen,
 502                                     union nf_inet_addr *rtp_addr)
 503{
 504        enum ip_conntrack_info ctinfo;
 505        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
 506        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
 507        u_int16_t port;
 508
 509        /* Connection will come from reply */
 510        if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
 511                             &ct->tuplehash[!dir].tuple.dst.u3))
 512                *rtp_addr = rtp_exp->tuple.dst.u3;
 513        else
 514                *rtp_addr = ct->tuplehash[!dir].tuple.dst.u3;
 515
 516        rtp_exp->saved_addr = rtp_exp->tuple.dst.u3;
 517        rtp_exp->tuple.dst.u3 = *rtp_addr;
 518        rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port;
 519        rtp_exp->dir = !dir;
 520        rtp_exp->expectfn = nf_nat_sip_expected;
 521
 522        rtcp_exp->saved_addr = rtcp_exp->tuple.dst.u3;
 523        rtcp_exp->tuple.dst.u3 = *rtp_addr;
 524        rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port;
 525        rtcp_exp->dir = !dir;
 526        rtcp_exp->expectfn = nf_nat_sip_expected;
 527
 528        /* Try to get same pair of ports: if not, try to change them. */
 529        for (port = ntohs(rtp_exp->tuple.dst.u.udp.port);
 530             port != 0; port += 2) {
 531                int ret;
 532
 533                rtp_exp->tuple.dst.u.udp.port = htons(port);
 534                ret = nf_ct_expect_related(rtp_exp);
 535                if (ret == -EBUSY)
 536                        continue;
 537                else if (ret < 0) {
 538                        port = 0;
 539                        break;
 540                }
 541                rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
 542                ret = nf_ct_expect_related(rtcp_exp);
 543                if (ret == 0)
 544                        break;
 545                else if (ret == -EBUSY) {
 546                        nf_ct_unexpect_related(rtp_exp);
 547                        continue;
 548                } else if (ret < 0) {
 549                        nf_ct_unexpect_related(rtp_exp);
 550                        port = 0;
 551                        break;
 552                }
 553        }
 554
 555        if (port == 0)
 556                goto err1;
 557
 558        /* Update media port. */
 559        if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
 560            !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
 561                             mediaoff, medialen, port))
 562                goto err2;
 563
 564        return NF_ACCEPT;
 565
 566err2:
 567        nf_ct_unexpect_related(rtp_exp);
 568        nf_ct_unexpect_related(rtcp_exp);
 569err1:
 570        return NF_DROP;
 571}
 572
 573static struct nf_ct_helper_expectfn sip_nat = {
 574        .name           = "sip",
 575        .expectfn       = nf_nat_sip_expected,
 576};
 577
 578static void __exit nf_nat_sip_fini(void)
 579{
 580        RCU_INIT_POINTER(nf_nat_sip_hook, NULL);
 581        RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL);
 582        RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL);
 583        RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL);
 584        RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL);
 585        RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL);
 586        RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL);
 587        nf_ct_helper_expectfn_unregister(&sip_nat);
 588        synchronize_rcu();
 589}
 590
 591static int __init nf_nat_sip_init(void)
 592{
 593        BUG_ON(nf_nat_sip_hook != NULL);
 594        BUG_ON(nf_nat_sip_seq_adjust_hook != NULL);
 595        BUG_ON(nf_nat_sip_expect_hook != NULL);
 596        BUG_ON(nf_nat_sdp_addr_hook != NULL);
 597        BUG_ON(nf_nat_sdp_port_hook != NULL);
 598        BUG_ON(nf_nat_sdp_session_hook != NULL);
 599        BUG_ON(nf_nat_sdp_media_hook != NULL);
 600        RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip);
 601        RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust);
 602        RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect);
 603        RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr);
 604        RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port);
 605        RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session);
 606        RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media);
 607        nf_ct_helper_expectfn_register(&sip_nat);
 608        return 0;
 609}
 610
 611module_init(nf_nat_sip_init);
 612module_exit(nf_nat_sip_fini);
 613
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.