1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * ebt_arp
4 *
5 * Authors:
6 * Bart De Schuymer <bdschuym@pandora.be>
7 * Tim Gardner <timg@tpi.com>
8 *
9 * April, 2002
10 *
11 */
12#include <linux/if_arp.h>
13#include <linux/if_ether.h>
14#include <linux/module.h>
15#include <linux/netfilter/x_tables.h>
16#include <linux/netfilter_bridge/ebtables.h>
17#include <linux/netfilter_bridge/ebt_arp.h>
18
19static bool
20ebt_arp_mt(const struct sk_buff *skb, struct xt_action_param *par)
21{
22 const struct ebt_arp_info *info = par->matchinfo;
23 const struct arphdr *ah;
24 struct arphdr _arph;
25
26 ah = skb_header_pointer(skb, offset: 0, len: sizeof(_arph), buffer: &_arph);
27 if (ah == NULL)
28 return false;
29 if ((info->bitmask & EBT_ARP_OPCODE) &&
30 NF_INVF(info, EBT_ARP_OPCODE, info->opcode != ah->ar_op))
31 return false;
32 if ((info->bitmask & EBT_ARP_HTYPE) &&
33 NF_INVF(info, EBT_ARP_HTYPE, info->htype != ah->ar_hrd))
34 return false;
35 if ((info->bitmask & EBT_ARP_PTYPE) &&
36 NF_INVF(info, EBT_ARP_PTYPE, info->ptype != ah->ar_pro))
37 return false;
38
39 if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_GRAT)) {
40 const __be32 *sap, *dap;
41 __be32 saddr, daddr;
42
43 if (ah->ar_pln != sizeof(__be32) || ah->ar_pro != htons(ETH_P_IP))
44 return false;
45 sap = skb_header_pointer(skb, offset: sizeof(struct arphdr) +
46 ah->ar_hln, len: sizeof(saddr),
47 buffer: &saddr);
48 if (sap == NULL)
49 return false;
50 dap = skb_header_pointer(skb, offset: sizeof(struct arphdr) +
51 2*ah->ar_hln+sizeof(saddr),
52 len: sizeof(daddr), buffer: &daddr);
53 if (dap == NULL)
54 return false;
55 if ((info->bitmask & EBT_ARP_SRC_IP) &&
56 NF_INVF(info, EBT_ARP_SRC_IP,
57 info->saddr != (*sap & info->smsk)))
58 return false;
59 if ((info->bitmask & EBT_ARP_DST_IP) &&
60 NF_INVF(info, EBT_ARP_DST_IP,
61 info->daddr != (*dap & info->dmsk)))
62 return false;
63 if ((info->bitmask & EBT_ARP_GRAT) &&
64 NF_INVF(info, EBT_ARP_GRAT, *dap != *sap))
65 return false;
66 }
67
68 if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
69 const unsigned char *mp;
70 unsigned char _mac[ETH_ALEN];
71
72 if (ah->ar_hln != ETH_ALEN || ah->ar_hrd != htons(ARPHRD_ETHER))
73 return false;
74 if (info->bitmask & EBT_ARP_SRC_MAC) {
75 mp = skb_header_pointer(skb, offset: sizeof(struct arphdr),
76 len: sizeof(_mac), buffer: &_mac);
77 if (mp == NULL)
78 return false;
79 if (NF_INVF(info, EBT_ARP_SRC_MAC,
80 !ether_addr_equal_masked(mp, info->smaddr,
81 info->smmsk)))
82 return false;
83 }
84
85 if (info->bitmask & EBT_ARP_DST_MAC) {
86 mp = skb_header_pointer(skb, offset: sizeof(struct arphdr) +
87 ah->ar_hln + ah->ar_pln,
88 len: sizeof(_mac), buffer: &_mac);
89 if (mp == NULL)
90 return false;
91 if (NF_INVF(info, EBT_ARP_DST_MAC,
92 !ether_addr_equal_masked(mp, info->dmaddr,
93 info->dmmsk)))
94 return false;
95 }
96 }
97
98 return true;
99}
100
101static int ebt_arp_mt_check(const struct xt_mtchk_param *par)
102{
103 const struct ebt_arp_info *info = par->matchinfo;
104 const struct ebt_entry *e = par->entryinfo;
105
106 if ((e->ethproto != htons(ETH_P_ARP) &&
107 e->ethproto != htons(ETH_P_RARP)) ||
108 e->invflags & EBT_IPROTO)
109 return -EINVAL;
110 if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
111 return -EINVAL;
112 return 0;
113}
114
115static struct xt_match ebt_arp_mt_reg __read_mostly = {
116 .name = "arp",
117 .revision = 0,
118 .family = NFPROTO_BRIDGE,
119 .match = ebt_arp_mt,
120 .checkentry = ebt_arp_mt_check,
121 .matchsize = sizeof(struct ebt_arp_info),
122 .me = THIS_MODULE,
123};
124
125static int __init ebt_arp_init(void)
126{
127 return xt_register_match(target: &ebt_arp_mt_reg);
128}
129
130static void __exit ebt_arp_fini(void)
131{
132 xt_unregister_match(target: &ebt_arp_mt_reg);
133}
134
135module_init(ebt_arp_init);
136module_exit(ebt_arp_fini);
137MODULE_DESCRIPTION("Ebtables: ARP protocol packet match");
138MODULE_LICENSE("GPL");
139

source code of linux/net/bridge/netfilter/ebt_arp.c