xref: /linux-6.15/net/netfilter/xt_physdev.c (revision eacc17fb)
1 /* Kernel module to match the bridge port in and
2  * out device for IP packets coming into contact with a bridge. */
3 
4 /* (C) 2001-2003 Bart De Schuymer <[email protected]>
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/skbuff.h>
13 #include <linux/netfilter_bridge.h>
14 #include <linux/netfilter/xt_physdev.h>
15 #include <linux/netfilter/x_tables.h>
16 
17 MODULE_LICENSE("GPL");
18 MODULE_AUTHOR("Bart De Schuymer <[email protected]>");
19 MODULE_DESCRIPTION("Xtables: Bridge physical device match");
20 MODULE_ALIAS("ipt_physdev");
21 MODULE_ALIAS("ip6t_physdev");
22 
23 static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
24 {
25 	const unsigned long *a = (const unsigned long *)_a;
26 	const unsigned long *b = (const unsigned long *)_b;
27 	const unsigned long *mask = (const unsigned long *)_mask;
28 	unsigned long ret;
29 
30 	ret = (a[0] ^ b[0]) & mask[0];
31 	if (IFNAMSIZ > sizeof(unsigned long))
32 		ret |= (a[1] ^ b[1]) & mask[1];
33 	if (IFNAMSIZ > 2 * sizeof(unsigned long))
34 		ret |= (a[2] ^ b[2]) & mask[2];
35 	if (IFNAMSIZ > 3 * sizeof(unsigned long))
36 		ret |= (a[3] ^ b[3]) & mask[3];
37 	BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
38 	return ret;
39 }
40 
41 static bool
42 physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
43 {
44 	static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
45 	const struct xt_physdev_info *info = par->matchinfo;
46 	unsigned long ret;
47 	const char *indev, *outdev;
48 	const struct nf_bridge_info *nf_bridge;
49 
50 	/* Not a bridged IP packet or no info available yet:
51 	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
52 	 * the destination device will be a bridge. */
53 	if (!(nf_bridge = skb->nf_bridge)) {
54 		/* Return MATCH if the invert flags of the used options are on */
55 		if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
56 		    !(info->invert & XT_PHYSDEV_OP_BRIDGED))
57 			return false;
58 		if ((info->bitmask & XT_PHYSDEV_OP_ISIN) &&
59 		    !(info->invert & XT_PHYSDEV_OP_ISIN))
60 			return false;
61 		if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) &&
62 		    !(info->invert & XT_PHYSDEV_OP_ISOUT))
63 			return false;
64 		if ((info->bitmask & XT_PHYSDEV_OP_IN) &&
65 		    !(info->invert & XT_PHYSDEV_OP_IN))
66 			return false;
67 		if ((info->bitmask & XT_PHYSDEV_OP_OUT) &&
68 		    !(info->invert & XT_PHYSDEV_OP_OUT))
69 			return false;
70 		return true;
71 	}
72 
73 	/* This only makes sense in the FORWARD and POSTROUTING chains */
74 	if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
75 	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
76 	    !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
77 		return false;
78 
79 	if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
80 	    (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
81 	    (info->bitmask & XT_PHYSDEV_OP_ISOUT &&
82 	    (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
83 		return false;
84 
85 	if (!(info->bitmask & XT_PHYSDEV_OP_IN))
86 		goto match_outdev;
87 	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
88 	ret = ifname_compare(indev, info->physindev, info->in_mask);
89 
90 	if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
91 		return false;
92 
93 match_outdev:
94 	if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
95 		return true;
96 	outdev = nf_bridge->physoutdev ?
97 		 nf_bridge->physoutdev->name : nulldevname;
98 	ret = ifname_compare(outdev, info->physoutdev, info->out_mask);
99 
100 	return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
101 }
102 
103 static bool physdev_mt_check(const struct xt_mtchk_param *par)
104 {
105 	const struct xt_physdev_info *info = par->matchinfo;
106 
107 	if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
108 	    info->bitmask & ~XT_PHYSDEV_OP_MASK)
109 		return false;
110 	if (info->bitmask & XT_PHYSDEV_OP_OUT &&
111 	    (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
112 	     info->invert & XT_PHYSDEV_OP_BRIDGED) &&
113 	    par->hook_mask & ((1 << NF_INET_LOCAL_OUT) |
114 	    (1 << NF_INET_FORWARD) | (1 << NF_INET_POST_ROUTING))) {
115 		printk(KERN_WARNING "physdev match: using --physdev-out in the "
116 		       "OUTPUT, FORWARD and POSTROUTING chains for non-bridged "
117 		       "traffic is not supported anymore.\n");
118 		if (par->hook_mask & (1 << NF_INET_LOCAL_OUT))
119 			return false;
120 	}
121 	return true;
122 }
123 
124 static struct xt_match physdev_mt_reg __read_mostly = {
125 	.name       = "physdev",
126 	.revision   = 0,
127 	.family     = NFPROTO_UNSPEC,
128 	.checkentry = physdev_mt_check,
129 	.match      = physdev_mt,
130 	.matchsize  = sizeof(struct xt_physdev_info),
131 	.me         = THIS_MODULE,
132 };
133 
134 static int __init physdev_mt_init(void)
135 {
136 	return xt_register_match(&physdev_mt_reg);
137 }
138 
139 static void __exit physdev_mt_exit(void)
140 {
141 	xt_unregister_match(&physdev_mt_reg);
142 }
143 
144 module_init(physdev_mt_init);
145 module_exit(physdev_mt_exit);
146