12e4e6a17SHarald Welte /* Kernel module to match the bridge port in and 22e4e6a17SHarald Welte * out device for IP packets coming into contact with a bridge. */ 32e4e6a17SHarald Welte 42e4e6a17SHarald Welte /* (C) 2001-2003 Bart De Schuymer <bdschuym@pandora.be> 52e4e6a17SHarald Welte * 62e4e6a17SHarald Welte * This program is free software; you can redistribute it and/or modify 72e4e6a17SHarald Welte * it under the terms of the GNU General Public License version 2 as 82e4e6a17SHarald Welte * published by the Free Software Foundation. 92e4e6a17SHarald Welte */ 102e4e6a17SHarald Welte 112e4e6a17SHarald Welte #include <linux/module.h> 122e4e6a17SHarald Welte #include <linux/skbuff.h> 13deb47c66SAndrew Morton #include <linux/netfilter_bridge.h> 142e4e6a17SHarald Welte #include <linux/netfilter/xt_physdev.h> 152e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h> 162e4e6a17SHarald Welte 172e4e6a17SHarald Welte MODULE_LICENSE("GPL"); 182e4e6a17SHarald Welte MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>"); 192ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: Bridge physical device match"); 202e4e6a17SHarald Welte MODULE_ALIAS("ipt_physdev"); 212e4e6a17SHarald Welte MODULE_ALIAS("ip6t_physdev"); 222e4e6a17SHarald Welte 23eacc17fbSEric Dumazet static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask) 24eacc17fbSEric Dumazet { 25eacc17fbSEric Dumazet const unsigned long *a = (const unsigned long *)_a; 26eacc17fbSEric Dumazet const unsigned long *b = (const unsigned long *)_b; 27eacc17fbSEric Dumazet const unsigned long *mask = (const unsigned long *)_mask; 28eacc17fbSEric Dumazet unsigned long ret; 29eacc17fbSEric Dumazet 30eacc17fbSEric Dumazet ret = (a[0] ^ b[0]) & mask[0]; 31eacc17fbSEric Dumazet if (IFNAMSIZ > sizeof(unsigned long)) 32eacc17fbSEric Dumazet ret |= (a[1] ^ b[1]) & mask[1]; 33eacc17fbSEric Dumazet if (IFNAMSIZ > 2 * sizeof(unsigned long)) 34eacc17fbSEric Dumazet ret |= (a[2] ^ b[2]) & mask[2]; 35eacc17fbSEric Dumazet if (IFNAMSIZ > 3 * sizeof(unsigned long)) 36eacc17fbSEric Dumazet ret |= (a[3] ^ b[3]) & mask[3]; 37eacc17fbSEric Dumazet BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long)); 38eacc17fbSEric Dumazet return ret; 39eacc17fbSEric Dumazet } 40eacc17fbSEric Dumazet 411d93a9cbSJan Engelhardt static bool 42f7108a20SJan Engelhardt physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) 432e4e6a17SHarald Welte { 444f1c3b7eSEric Dumazet static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); 45f7108a20SJan Engelhardt const struct xt_physdev_info *info = par->matchinfo; 464f1c3b7eSEric Dumazet unsigned long ret; 472e4e6a17SHarald Welte const char *indev, *outdev; 48a47362a2SJan Engelhardt const struct nf_bridge_info *nf_bridge; 492e4e6a17SHarald Welte 502e4e6a17SHarald Welte /* Not a bridged IP packet or no info available yet: 512e4e6a17SHarald Welte * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if 522e4e6a17SHarald Welte * the destination device will be a bridge. */ 532e4e6a17SHarald Welte if (!(nf_bridge = skb->nf_bridge)) { 542e4e6a17SHarald Welte /* Return MATCH if the invert flags of the used options are on */ 552e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) && 562e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_BRIDGED)) 571d93a9cbSJan Engelhardt return false; 582e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_ISIN) && 592e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_ISIN)) 601d93a9cbSJan Engelhardt return false; 612e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) && 622e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_ISOUT)) 631d93a9cbSJan Engelhardt return false; 642e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_IN) && 652e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_IN)) 661d93a9cbSJan Engelhardt return false; 672e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_OUT) && 682e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_OUT)) 691d93a9cbSJan Engelhardt return false; 701d93a9cbSJan Engelhardt return true; 712e4e6a17SHarald Welte } 722e4e6a17SHarald Welte 732e4e6a17SHarald Welte /* This only makes sense in the FORWARD and POSTROUTING chains */ 742e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) && 752e4e6a17SHarald Welte (!!(nf_bridge->mask & BRNF_BRIDGED) ^ 762e4e6a17SHarald Welte !(info->invert & XT_PHYSDEV_OP_BRIDGED))) 771d93a9cbSJan Engelhardt return false; 782e4e6a17SHarald Welte 792e4e6a17SHarald Welte if ((info->bitmask & XT_PHYSDEV_OP_ISIN && 802e4e6a17SHarald Welte (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) || 812e4e6a17SHarald Welte (info->bitmask & XT_PHYSDEV_OP_ISOUT && 822e4e6a17SHarald Welte (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT)))) 831d93a9cbSJan Engelhardt return false; 842e4e6a17SHarald Welte 852e4e6a17SHarald Welte if (!(info->bitmask & XT_PHYSDEV_OP_IN)) 862e4e6a17SHarald Welte goto match_outdev; 872e4e6a17SHarald Welte indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname; 88eacc17fbSEric Dumazet ret = ifname_compare(indev, info->physindev, info->in_mask); 892e4e6a17SHarald Welte 901d93a9cbSJan Engelhardt if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN)) 911d93a9cbSJan Engelhardt return false; 922e4e6a17SHarald Welte 932e4e6a17SHarald Welte match_outdev: 942e4e6a17SHarald Welte if (!(info->bitmask & XT_PHYSDEV_OP_OUT)) 951d93a9cbSJan Engelhardt return true; 962e4e6a17SHarald Welte outdev = nf_bridge->physoutdev ? 972e4e6a17SHarald Welte nf_bridge->physoutdev->name : nulldevname; 98eacc17fbSEric Dumazet ret = ifname_compare(outdev, info->physoutdev, info->out_mask); 99eacc17fbSEric Dumazet 1004f1c3b7eSEric Dumazet return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT)); 1012e4e6a17SHarald Welte } 1022e4e6a17SHarald Welte 1039b4fce7aSJan Engelhardt static bool physdev_mt_check(const struct xt_mtchk_param *par) 1042e4e6a17SHarald Welte { 1059b4fce7aSJan Engelhardt const struct xt_physdev_info *info = par->matchinfo; 1062e4e6a17SHarald Welte 1072e4e6a17SHarald Welte if (!(info->bitmask & XT_PHYSDEV_OP_MASK) || 1082e4e6a17SHarald Welte info->bitmask & ~XT_PHYSDEV_OP_MASK) 109ccb79bdcSJan Engelhardt return false; 1102bf540b7SPatrick McHardy if (info->bitmask & XT_PHYSDEV_OP_OUT && 11110ea6ac8SPatrick McHardy (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || 11210ea6ac8SPatrick McHardy info->invert & XT_PHYSDEV_OP_BRIDGED) && 1139b4fce7aSJan Engelhardt par->hook_mask & ((1 << NF_INET_LOCAL_OUT) | 1149b4fce7aSJan Engelhardt (1 << NF_INET_FORWARD) | (1 << NF_INET_POST_ROUTING))) { 11510ea6ac8SPatrick McHardy printk(KERN_WARNING "physdev match: using --physdev-out in the " 11610ea6ac8SPatrick McHardy "OUTPUT, FORWARD and POSTROUTING chains for non-bridged " 1172bf540b7SPatrick McHardy "traffic is not supported anymore.\n"); 1189b4fce7aSJan Engelhardt if (par->hook_mask & (1 << NF_INET_LOCAL_OUT)) 119ccb79bdcSJan Engelhardt return false; 12010ea6ac8SPatrick McHardy } 121ccb79bdcSJan Engelhardt return true; 1222e4e6a17SHarald Welte } 1232e4e6a17SHarald Welte 124ab4f21e6SJan Engelhardt static struct xt_match physdev_mt_reg __read_mostly = { 1252e4e6a17SHarald Welte .name = "physdev", 126ab4f21e6SJan Engelhardt .revision = 0, 127ab4f21e6SJan Engelhardt .family = NFPROTO_UNSPEC, 128d3c5ee6dSJan Engelhardt .checkentry = physdev_mt_check, 129d3c5ee6dSJan Engelhardt .match = physdev_mt, 1305d04bff0SPatrick McHardy .matchsize = sizeof(struct xt_physdev_info), 1312e4e6a17SHarald Welte .me = THIS_MODULE, 1322e4e6a17SHarald Welte }; 1332e4e6a17SHarald Welte 134d3c5ee6dSJan Engelhardt static int __init physdev_mt_init(void) 1352e4e6a17SHarald Welte { 136ab4f21e6SJan Engelhardt return xt_register_match(&physdev_mt_reg); 1372e4e6a17SHarald Welte } 1382e4e6a17SHarald Welte 139d3c5ee6dSJan Engelhardt static void __exit physdev_mt_exit(void) 1402e4e6a17SHarald Welte { 141ab4f21e6SJan Engelhardt xt_unregister_match(&physdev_mt_reg); 1422e4e6a17SHarald Welte } 1432e4e6a17SHarald Welte 144d3c5ee6dSJan Engelhardt module_init(physdev_mt_init); 145d3c5ee6dSJan Engelhardt module_exit(physdev_mt_exit); 146