xref: /openbmc/linux/net/netfilter/xt_physdev.c (revision eacc17fb)
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