/* * nf_conntrack_ssdp.c - netfilter connection tracking helper for UPnP SSDP * * Copyright 2012 Ian Pilcher * * This program is free software. You can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. */ #include #include #include #include #include #define SSDP_MCAST_ADDR 0xeffffffa /* 239.255.255.250 - host byte order */ #define SSDP_UDP_PORT 1900 #define SSDP_M_SEARCH "M-SEARCH" #define SSDP_M_SEARCH_SIZE (sizeof SSDP_M_SEARCH - 1) MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Ian Pilcher "); MODULE_DESCRIPTION("SSDP connection tracking helper"); MODULE_ALIAS("ip_conntrack_ssdp"); MODULE_ALIAS_NFCT_HELPER("ssdp"); static int ssdp_help(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct nf_conntrack_expect *expect; struct nf_conntrack_tuple *tuple; char udpdata_buffer[SSDP_M_SEARCH_SIZE]; char *udpdata; tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; pr_debug("ssdp_help: %pI4:%hu --> %pI4:%hu\n", &tuple->src.u3.ip, be16_to_cpu(tuple->src.u.udp.port), &tuple->dst.u3.ip, be16_to_cpu(tuple->dst.u.udp.port)); if (tuple->dst.u3.ip != cpu_to_be32(SSDP_MCAST_ADDR)) { pr_debug("ssdp_help: destination address != 239.255.255.250; ignoring\n"); return NF_ACCEPT; } udpdata = skb_header_pointer(skb, protoff + sizeof(struct udphdr), sizeof udpdata_buffer, &udpdata_buffer); if (udpdata == NULL) { pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n"); return NF_ACCEPT; } if (memcmp(udpdata, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) { pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n"); return NF_ACCEPT; } if ((expect = nf_ct_expect_alloc(ct)) == NULL) { pr_warn("Memory allocation failure\n"); return NF_DROP; } expect->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; memset(&expect->mask, 0, sizeof expect->mask); expect->mask.src.u.udp.port = 0xffff; /* byte order doesn't matter */ expect->expectfn = NULL; expect->flags = 0; expect->class = NF_CT_EXPECT_CLASS_DEFAULT; expect->helper = NULL; nf_ct_expect_related(expect); nf_ct_expect_put(expect); return NF_ACCEPT; } static const struct nf_conntrack_expect_policy ssdp_policy = { .max_expected = 1, .timeout = 1, .name = "ssdp", }; static struct nf_conntrack_helper __read_mostly ssdp_helper = { .name = "ssdp", .tuple.src.l3num = NFPROTO_IPV4, .tuple.src.u.udp.port = cpu_to_be16(SSDP_UDP_PORT), .tuple.dst.protonum = IPPROTO_UDP, .me = THIS_MODULE, .help = ssdp_help, .expect_policy = &ssdp_policy, }; static int __init nf_conntrack_ssdp_init(void) { return nf_conntrack_helper_register(&ssdp_helper); } static void __exit nf_conntrack_ssdp_exit(void) { nf_conntrack_helper_unregister(&ssdp_helper); } module_init(nf_conntrack_ssdp_init); module_exit(nf_conntrack_ssdp_exit);