xref: /linux-6.15/net/netfilter/xt_physdev.c (revision c4399016)
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 #include <linux/netfilter_bridge.h>
17 
18 MODULE_LICENSE("GPL");
19 MODULE_AUTHOR("Bart De Schuymer <[email protected]>");
20 MODULE_DESCRIPTION("iptables bridge physical device match module");
21 MODULE_ALIAS("ipt_physdev");
22 MODULE_ALIAS("ip6t_physdev");
23 
24 static bool
25 match(const struct sk_buff *skb,
26       const struct net_device *in,
27       const struct net_device *out,
28       const struct xt_match *match,
29       const void *matchinfo,
30       int offset,
31       unsigned int protoff,
32       bool *hotdrop)
33 {
34 	int i;
35 	static const char nulldevname[IFNAMSIZ];
36 	const struct xt_physdev_info *info = matchinfo;
37 	bool ret;
38 	const char *indev, *outdev;
39 	const struct nf_bridge_info *nf_bridge;
40 
41 	/* Not a bridged IP packet or no info available yet:
42 	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
43 	 * the destination device will be a bridge. */
44 	if (!(nf_bridge = skb->nf_bridge)) {
45 		/* Return MATCH if the invert flags of the used options are on */
46 		if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
47 		    !(info->invert & XT_PHYSDEV_OP_BRIDGED))
48 			return false;
49 		if ((info->bitmask & XT_PHYSDEV_OP_ISIN) &&
50 		    !(info->invert & XT_PHYSDEV_OP_ISIN))
51 			return false;
52 		if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) &&
53 		    !(info->invert & XT_PHYSDEV_OP_ISOUT))
54 			return false;
55 		if ((info->bitmask & XT_PHYSDEV_OP_IN) &&
56 		    !(info->invert & XT_PHYSDEV_OP_IN))
57 			return false;
58 		if ((info->bitmask & XT_PHYSDEV_OP_OUT) &&
59 		    !(info->invert & XT_PHYSDEV_OP_OUT))
60 			return false;
61 		return true;
62 	}
63 
64 	/* This only makes sense in the FORWARD and POSTROUTING chains */
65 	if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
66 	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
67 	    !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
68 		return false;
69 
70 	if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
71 	    (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
72 	    (info->bitmask & XT_PHYSDEV_OP_ISOUT &&
73 	    (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
74 		return false;
75 
76 	if (!(info->bitmask & XT_PHYSDEV_OP_IN))
77 		goto match_outdev;
78 	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
79 	for (i = 0, ret = false; i < IFNAMSIZ/sizeof(unsigned int); i++) {
80 		ret |= (((const unsigned int *)indev)[i]
81 			^ ((const unsigned int *)info->physindev)[i])
82 			& ((const unsigned int *)info->in_mask)[i];
83 	}
84 
85 	if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
86 		return false;
87 
88 match_outdev:
89 	if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
90 		return true;
91 	outdev = nf_bridge->physoutdev ?
92 		 nf_bridge->physoutdev->name : nulldevname;
93 	for (i = 0, ret = false; i < IFNAMSIZ/sizeof(unsigned int); i++) {
94 		ret |= (((const unsigned int *)outdev)[i]
95 			^ ((const unsigned int *)info->physoutdev)[i])
96 			& ((const unsigned int *)info->out_mask)[i];
97 	}
98 
99 	return ret ^ !(info->invert & XT_PHYSDEV_OP_OUT);
100 }
101 
102 static bool
103 checkentry(const char *tablename,
104 		       const void *ip,
105 		       const struct xt_match *match,
106 		       void *matchinfo,
107 		       unsigned int hook_mask)
108 {
109 	const struct xt_physdev_info *info = matchinfo;
110 
111 	if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
112 	    info->bitmask & ~XT_PHYSDEV_OP_MASK)
113 		return false;
114 	if (info->bitmask & XT_PHYSDEV_OP_OUT &&
115 	    (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
116 	     info->invert & XT_PHYSDEV_OP_BRIDGED) &&
117 	    hook_mask & ((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_FORWARD) |
118 			 (1 << NF_IP_POST_ROUTING))) {
119 		printk(KERN_WARNING "physdev match: using --physdev-out in the "
120 		       "OUTPUT, FORWARD and POSTROUTING chains for non-bridged "
121 		       "traffic is not supported anymore.\n");
122 		if (hook_mask & (1 << NF_IP_LOCAL_OUT))
123 			return false;
124 	}
125 	return true;
126 }
127 
128 static struct xt_match xt_physdev_match[] __read_mostly = {
129 	{
130 		.name		= "physdev",
131 		.family		= AF_INET,
132 		.checkentry	= checkentry,
133 		.match		= match,
134 		.matchsize	= sizeof(struct xt_physdev_info),
135 		.me		= THIS_MODULE,
136 	},
137 	{
138 		.name		= "physdev",
139 		.family		= AF_INET6,
140 		.checkentry	= checkentry,
141 		.match		= match,
142 		.matchsize	= sizeof(struct xt_physdev_info),
143 		.me		= THIS_MODULE,
144 	},
145 };
146 
147 static int __init xt_physdev_init(void)
148 {
149 	return xt_register_matches(xt_physdev_match,
150 				   ARRAY_SIZE(xt_physdev_match));
151 }
152 
153 static void __exit xt_physdev_fini(void)
154 {
155 	xt_unregister_matches(xt_physdev_match, ARRAY_SIZE(xt_physdev_match));
156 }
157 
158 module_init(xt_physdev_init);
159 module_exit(xt_physdev_fini);
160