The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net/ipv4/netfilter/ip_conntrack_sip.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* SIP extension for IP connection tracking.
    2  *
    3  * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
    4  * based on RR's ip_conntrack_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/ctype.h>
   13 #include <linux/skbuff.h>
   14 #include <linux/in.h>
   15 #include <linux/ip.h>
   16 #include <linux/udp.h>
   17 
   18 #include <linux/netfilter.h>
   19 #include <linux/netfilter_ipv4.h>
   20 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
   21 #include <linux/netfilter_ipv4/ip_conntrack_sip.h>
   22 
   23 #if 0
   24 #define DEBUGP printk
   25 #else
   26 #define DEBUGP(format, args...)
   27 #endif
   28 
   29 MODULE_LICENSE("GPL");
   30 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
   31 MODULE_DESCRIPTION("SIP connection tracking helper");
   32 
   33 #define MAX_PORTS       8
   34 static unsigned short ports[MAX_PORTS];
   35 static int ports_c;
   36 module_param_array(ports, ushort, &ports_c, 0400);
   37 MODULE_PARM_DESC(ports, "port numbers of sip servers");
   38 
   39 static unsigned int sip_timeout = SIP_TIMEOUT;
   40 module_param(sip_timeout, uint, 0600);
   41 MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
   42 
   43 unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb,
   44                                 enum ip_conntrack_info ctinfo,
   45                                 struct ip_conntrack *ct,
   46                                 const char **dptr);
   47 EXPORT_SYMBOL_GPL(ip_nat_sip_hook);
   48 
   49 unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb,
   50                                 enum ip_conntrack_info ctinfo,
   51                                 struct ip_conntrack_expect *exp,
   52                                 const char *dptr);
   53 EXPORT_SYMBOL_GPL(ip_nat_sdp_hook);
   54 
   55 static int digits_len(const char *dptr, const char *limit, int *shift);
   56 static int epaddr_len(const char *dptr, const char *limit, int *shift);
   57 static int skp_digits_len(const char *dptr, const char *limit, int *shift);
   58 static int skp_epaddr_len(const char *dptr, const char *limit, int *shift);
   59 
   60 struct sip_header_nfo {
   61         const char      *lname;
   62         const char      *sname;
   63         const char      *ln_str;
   64         size_t          lnlen;
   65         size_t          snlen;
   66         size_t          ln_strlen;
   67         int             case_sensitive;
   68         int             (*match_len)(const char *, const char *, int *);
   69 };
   70 
   71 static struct sip_header_nfo ct_sip_hdrs[] = {
   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 */
   80                 .lname          = "sip:",
   81                 .lnlen          = sizeof("sip:") - 1,
   82                 .ln_str         = "@",
   83                 .ln_strlen      = sizeof("@") - 1,
   84                 .match_len      = epaddr_len
   85         },
   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         },
  104         [POS_VIA] = {           /* SIP Via header */
  105                 .lname          = "Via:",
  106                 .lnlen          = sizeof("Via:") - 1,
  107                 .sname          = "\r\nv:",
  108                 .snlen          = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
  109                 .ln_str         = "UDP ",
  110                 .ln_strlen      = sizeof("UDP ") - 1,
  111                 .match_len      = epaddr_len,
  112         },
  113         [POS_CONTACT] = {       /* SIP Contact header */
  114                 .lname          = "Contact:",
  115                 .lnlen          = sizeof("Contact:") - 1,
  116                 .sname          = "\r\nm:",
  117                 .snlen          = sizeof("\r\nm:") - 1,
  118                 .ln_str         = "sip:",
  119                 .ln_strlen      = sizeof("sip:") - 1,
  120                 .match_len      = skp_epaddr_len
  121         },
  122         [POS_CONTENT] = {       /* SIP Content length header */
  123                 .lname          = "Content-Length:",
  124                 .lnlen          = sizeof("Content-Length:") - 1,
  125                 .sname          = "\r\nl:",
  126                 .snlen          = sizeof("\r\nl:") - 1,
  127                 .ln_str         = ":",
  128                 .ln_strlen      = sizeof(":") - 1,
  129                 .match_len      = skp_digits_len
  130         },
  131         [POS_MEDIA] = {         /* SDP media info */
  132                 .case_sensitive = 1,
  133                 .lname          = "\nm=",
  134                 .lnlen          = sizeof("\nm=") - 1,
  135                 .sname          = "\rm=",
  136                 .snlen          = sizeof("\rm=") - 1,
  137                 .ln_str         = "audio ",
  138                 .ln_strlen      = sizeof("audio ") - 1,
  139                 .match_len      = digits_len
  140         },
  141         [POS_OWNER] = {         /* SDP owner address*/
  142                 .case_sensitive = 1,
  143                 .lname          = "\no=",
  144                 .lnlen          = sizeof("\no=") - 1,
  145                 .sname          = "\ro=",
  146                 .snlen          = sizeof("\ro=") - 1,
  147                 .ln_str         = "IN IP4 ",
  148                 .ln_strlen      = sizeof("IN IP4 ") - 1,
  149                 .match_len      = epaddr_len
  150         },
  151         [POS_CONNECTION] = {    /* SDP connection info */
  152                 .case_sensitive = 1,
  153                 .lname          = "\nc=",
  154                 .lnlen          = sizeof("\nc=") - 1,
  155                 .sname          = "\rc=",
  156                 .snlen          = sizeof("\rc=") - 1,
  157                 .ln_str         = "IN IP4 ",
  158                 .ln_strlen      = sizeof("IN IP4 ") - 1,
  159                 .match_len      = epaddr_len
  160         },
  161         [POS_SDP_HEADER] = {    /* SDP version header */
  162                 .case_sensitive = 1,
  163                 .lname          = "\nv=",
  164                 .lnlen          = sizeof("\nv=") - 1,
  165                 .sname          = "\rv=",
  166                 .snlen          = sizeof("\rv=") - 1,
  167                 .ln_str         = "=",
  168                 .ln_strlen      = sizeof("=") - 1,
  169                 .match_len      = digits_len
  170         }
  171 };
  172 
  173 /* get line lenght until first CR or LF seen. */
  174 int ct_sip_lnlen(const char *line, const char *limit)
  175 {
  176         const char *k = line;
  177 
  178         while ((line <= limit) && (*line == '\r' || *line == '\n'))
  179                 line++;
  180 
  181         while (line <= limit) {
  182                 if (*line == '\r' || *line == '\n')
  183                         break;
  184                 line++;
  185         }
  186         return line - k;
  187 }
  188 EXPORT_SYMBOL_GPL(ct_sip_lnlen);
  189 
  190 /* Linear string search, case sensitive. */
  191 const char *ct_sip_search(const char *needle, const char *haystack,
  192                           size_t needle_len, size_t haystack_len,
  193                           int case_sensitive)
  194 {
  195         const char *limit = haystack + (haystack_len - needle_len);
  196 
  197         while (haystack <= limit) {
  198                 if (case_sensitive) {
  199                         if (strncmp(haystack, needle, needle_len) == 0)
  200                                 return haystack;
  201                 } else {
  202                         if (strnicmp(haystack, needle, needle_len) == 0)
  203                                 return haystack;
  204                 }
  205                 haystack++;
  206         }
  207         return NULL;
  208 }
  209 EXPORT_SYMBOL_GPL(ct_sip_search);
  210 
  211 static int digits_len(const char *dptr, const char *limit, int *shift)
  212 {
  213         int len = 0;
  214         while (dptr <= limit && isdigit(*dptr)) {
  215                 dptr++;
  216                 len++;
  217         }
  218         return len;
  219 }
  220 
  221 /* get digits lenght, skiping blank spaces. */
  222 static int skp_digits_len(const char *dptr, const char *limit, int *shift)
  223 {
  224         for (; dptr <= limit && *dptr == ' '; dptr++)
  225                 (*shift)++;
  226 
  227         return digits_len(dptr, limit, shift);
  228 }
  229 
  230 /* Simple ipaddr parser.. */
  231 static int parse_ipaddr(const char *cp, const char **endp,
  232                         __be32 *ipaddr, const char *limit)
  233 {
  234         unsigned long int val;
  235         int i, digit = 0;
  236 
  237         for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) {
  238                 digit = 0;
  239                 if (!isdigit(*cp))
  240                         break;
  241 
  242                 val = simple_strtoul(cp, (char **)&cp, 10);
  243                 if (val > 0xFF)
  244                         return -1;
  245 
  246                 ((u_int8_t *)ipaddr)[i] = val;
  247                 digit = 1;
  248 
  249                 if (*cp != '.')
  250                         break;
  251                 cp++;
  252         }
  253         if (!digit)
  254                 return -1;
  255 
  256         if (endp)
  257                 *endp = cp;
  258 
  259         return 0;
  260 }
  261 
  262 /* skip ip address. returns it lenght. */
  263 static int epaddr_len(const char *dptr, const char *limit, int *shift)
  264 {
  265         const char *aux = dptr;
  266         __be32 ip;
  267 
  268         if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) {
  269                 DEBUGP("ip: %s parse failed.!\n", dptr);
  270                 return 0;
  271         }
  272 
  273         /* Port number */
  274         if (*dptr == ':') {
  275                 dptr++;
  276                 dptr += digits_len(dptr, limit, shift);
  277         }
  278         return dptr - aux;
  279 }
  280 
  281 /* get address length, skiping user info. */
  282 static int skp_epaddr_len(const char *dptr, const char *limit, int *shift)
  283 {
  284         int s = *shift;
  285 
  286         /* Search for @, but stop at the end of the line.
  287          * We are inside a sip: URI, so we don't need to worry about
  288          * continuation lines. */
  289         while (dptr <= limit &&
  290                *dptr != '@' && *dptr != '\r' && *dptr != '\n') {
  291                 (*shift)++;
  292                 dptr++;
  293         }
  294 
  295         if (dptr <= limit && *dptr == '@') {
  296                 dptr++;
  297                 (*shift)++;
  298         } else
  299                 *shift = s;
  300 
  301         return epaddr_len(dptr, limit, shift);
  302 }
  303 
  304 /* Returns 0 if not found, -1 error parsing. */
  305 int ct_sip_get_info(const char *dptr, size_t dlen,
  306                     unsigned int *matchoff,
  307                     unsigned int *matchlen,
  308                     enum sip_header_pos pos)
  309 {
  310         struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];
  311         const char *limit, *aux, *k = dptr;
  312         int shift = 0;
  313 
  314         limit = dptr + (dlen - hnfo->lnlen);
  315 
  316         while (dptr <= limit) {
  317                 if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
  318                     (hnfo->sname == NULL ||
  319                      strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
  320                         dptr++;
  321                         continue;
  322                 }
  323                 aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
  324                                     ct_sip_lnlen(dptr, limit),
  325                                     hnfo->case_sensitive);
  326                 if (!aux) {
  327                         DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str,
  328                                hnfo->lname);
  329                         return -1;
  330                 }
  331                 aux += hnfo->ln_strlen;
  332 
  333                 *matchlen = hnfo->match_len(aux, limit, &shift);
  334                 if (!*matchlen)
  335                         return -1;
  336 
  337                 *matchoff = (aux - k) + shift;
  338 
  339                 DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname,
  340                        *matchlen);
  341                 return 1;
  342         }
  343         DEBUGP("%s header not found.\n", hnfo->lname);
  344         return 0;
  345 }
  346 EXPORT_SYMBOL_GPL(ct_sip_get_info);
  347 
  348 static int set_expected_rtp(struct sk_buff **pskb,
  349                             struct ip_conntrack *ct,
  350                             enum ip_conntrack_info ctinfo,
  351                             __be32 ipaddr, u_int16_t port,
  352                             const char *dptr)
  353 {
  354         struct ip_conntrack_expect *exp;
  355         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
  356         int ret;
  357         typeof(ip_nat_sdp_hook) ip_nat_sdp;
  358 
  359         exp = ip_conntrack_expect_alloc(ct);
  360         if (exp == NULL)
  361                 return NF_DROP;
  362 
  363         exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
  364         exp->tuple.src.u.udp.port = 0;
  365         exp->tuple.dst.ip = ipaddr;
  366         exp->tuple.dst.u.udp.port = htons(port);
  367         exp->tuple.dst.protonum = IPPROTO_UDP;
  368 
  369         exp->mask.src.ip = htonl(0xFFFFFFFF);
  370         exp->mask.src.u.udp.port = 0;
  371         exp->mask.dst.ip = htonl(0xFFFFFFFF);
  372         exp->mask.dst.u.udp.port = htons(0xFFFF);
  373         exp->mask.dst.protonum = 0xFF;
  374 
  375         exp->expectfn = NULL;
  376         exp->flags = 0;
  377 
  378         ip_nat_sdp = rcu_dereference(ip_nat_sdp_hook);
  379         if (ip_nat_sdp)
  380                 ret = ip_nat_sdp(pskb, ctinfo, exp, dptr);
  381         else {
  382                 if (ip_conntrack_expect_related(exp) != 0)
  383                         ret = NF_DROP;
  384                 else
  385                         ret = NF_ACCEPT;
  386         }
  387         ip_conntrack_expect_put(exp);
  388 
  389         return ret;
  390 }
  391 
  392 static int sip_help(struct sk_buff **pskb,
  393                     struct ip_conntrack *ct,
  394                     enum ip_conntrack_info ctinfo)
  395 {
  396         unsigned int dataoff, datalen;
  397         const char *dptr;
  398         int ret = NF_ACCEPT;
  399         int matchoff, matchlen;
  400         __be32 ipaddr;
  401         u_int16_t port;
  402         typeof(ip_nat_sip_hook) ip_nat_sip;
  403 
  404         /* No Data ? */
  405         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
  406         if (dataoff >= (*pskb)->len) {
  407                 DEBUGP("skb->len = %u\n", (*pskb)->len);
  408                 return NF_ACCEPT;
  409         }
  410 
  411         ip_ct_refresh(ct, *pskb, sip_timeout * HZ);
  412 
  413         if (!skb_is_nonlinear(*pskb))
  414                 dptr = (*pskb)->data + dataoff;
  415         else {
  416                 DEBUGP("Copy of skbuff not supported yet.\n");
  417                 goto out;
  418         }
  419 
  420         ip_nat_sip = rcu_dereference(ip_nat_sip_hook);
  421         if (ip_nat_sip) {
  422                 if (!ip_nat_sip(pskb, ctinfo, ct, &dptr)) {
  423                         ret = NF_DROP;
  424                         goto out;
  425                 }
  426         }
  427 
  428         /* After this point NAT, could have mangled skb, so
  429            we need to recalculate payload lenght. */
  430         datalen = (*pskb)->len - dataoff;
  431 
  432         if (datalen < (sizeof("SIP/2.0 200") - 1))
  433                 goto out;
  434 
  435         /* RTP info only in some SDP pkts */
  436         if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&
  437             memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {
  438                 goto out;
  439         }
  440         /* Get ip and port address from SDP packet. */
  441         if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
  442                             POS_CONNECTION) > 0) {
  443 
  444                 /* We'll drop only if there are parse problems. */
  445                 if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr,
  446                                  dptr + datalen) < 0) {
  447                         ret = NF_DROP;
  448                         goto out;
  449                 }
  450                 if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
  451                                     POS_MEDIA) > 0) {
  452 
  453                         port = simple_strtoul(dptr + matchoff, NULL, 10);
  454                         if (port < 1024) {
  455                                 ret = NF_DROP;
  456                                 goto out;
  457                         }
  458                         ret = set_expected_rtp(pskb, ct, ctinfo,
  459                                                ipaddr, port, dptr);
  460                 }
  461         }
  462 out:
  463         return ret;
  464 }
  465 
  466 static struct ip_conntrack_helper sip[MAX_PORTS];
  467 static char sip_names[MAX_PORTS][10];
  468 
  469 static void fini(void)
  470 {
  471         int i;
  472         for (i = 0; i < ports_c; i++) {
  473                 DEBUGP("unregistering helper for port %d\n", ports[i]);
  474                 ip_conntrack_helper_unregister(&sip[i]);
  475         }
  476 }
  477 
  478 static int __init init(void)
  479 {
  480         int i, ret;
  481         char *tmpname;
  482 
  483         if (ports_c == 0)
  484                 ports[ports_c++] = SIP_PORT;
  485 
  486         for (i = 0; i < ports_c; i++) {
  487                 /* Create helper structure */
  488                 memset(&sip[i], 0, sizeof(struct ip_conntrack_helper));
  489 
  490                 sip[i].tuple.dst.protonum = IPPROTO_UDP;
  491                 sip[i].tuple.src.u.udp.port = htons(ports[i]);
  492                 sip[i].mask.src.u.udp.port = htons(0xFFFF);
  493                 sip[i].mask.dst.protonum = 0xFF;
  494                 sip[i].max_expected = 2;
  495                 sip[i].timeout = 3 * 60; /* 3 minutes */
  496                 sip[i].me = THIS_MODULE;
  497                 sip[i].help = sip_help;
  498 
  499                 tmpname = &sip_names[i][0];
  500                 if (ports[i] == SIP_PORT)
  501                         sprintf(tmpname, "sip");
  502                 else
  503                         sprintf(tmpname, "sip-%d", i);
  504                 sip[i].name = tmpname;
  505 
  506                 DEBUGP("port #%d: %d\n", i, ports[i]);
  507 
  508                 ret = ip_conntrack_helper_register(&sip[i]);
  509                 if (ret) {
  510                         printk("ERROR registering helper for port %d\n",
  511                                 ports[i]);
  512                         fini();
  513                         return ret;
  514                 }
  515         }
  516         return 0;
  517 }
  518 
  519 module_init(init);
  520 module_exit(fini);

Cache object: 9d136bc2e4ca0e9ca6a042fca0194070


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.