1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ebt_ip6 |
4 | * |
5 | * Authors: |
6 | * Manohar Castelino <manohar.r.castelino@intel.com> |
7 | * Kuo-Lang Tseng <kuo-lang.tseng@intel.com> |
8 | * Jan Engelhardt <jengelh@medozas.de> |
9 | * |
10 | * Summary: |
11 | * This is just a modification of the IPv4 code written by |
12 | * Bart De Schuymer <bdschuym@pandora.be> |
13 | * with the changes required to support IPv6 |
14 | * |
15 | * Jan, 2008 |
16 | */ |
17 | #include <linux/ipv6.h> |
18 | #include <net/ipv6.h> |
19 | #include <linux/in.h> |
20 | #include <linux/module.h> |
21 | #include <net/dsfield.h> |
22 | #include <linux/netfilter/x_tables.h> |
23 | #include <linux/netfilter_bridge/ebtables.h> |
24 | #include <linux/netfilter_bridge/ebt_ip6.h> |
25 | |
26 | union pkthdr { |
27 | struct { |
28 | __be16 src; |
29 | __be16 dst; |
30 | } tcpudphdr; |
31 | struct { |
32 | u8 type; |
33 | u8 code; |
34 | } icmphdr; |
35 | }; |
36 | |
37 | static bool |
38 | ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) |
39 | { |
40 | const struct ebt_ip6_info *info = par->matchinfo; |
41 | const struct ipv6hdr *ih6; |
42 | struct ipv6hdr _ip6h; |
43 | const union pkthdr *pptr; |
44 | union pkthdr _pkthdr; |
45 | |
46 | ih6 = skb_header_pointer(skb, offset: 0, len: sizeof(_ip6h), buffer: &_ip6h); |
47 | if (ih6 == NULL) |
48 | return false; |
49 | if ((info->bitmask & EBT_IP6_TCLASS) && |
50 | NF_INVF(info, EBT_IP6_TCLASS, |
51 | info->tclass != ipv6_get_dsfield(ih6))) |
52 | return false; |
53 | if (((info->bitmask & EBT_IP6_SOURCE) && |
54 | NF_INVF(info, EBT_IP6_SOURCE, |
55 | ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk, |
56 | &info->saddr))) || |
57 | ((info->bitmask & EBT_IP6_DEST) && |
58 | NF_INVF(info, EBT_IP6_DEST, |
59 | ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk, |
60 | &info->daddr)))) |
61 | return false; |
62 | if (info->bitmask & EBT_IP6_PROTO) { |
63 | uint8_t nexthdr = ih6->nexthdr; |
64 | __be16 frag_off; |
65 | int offset_ph; |
66 | |
67 | offset_ph = ipv6_skip_exthdr(skb, start: sizeof(_ip6h), nexthdrp: &nexthdr, frag_offp: &frag_off); |
68 | if (offset_ph == -1) |
69 | return false; |
70 | if (NF_INVF(info, EBT_IP6_PROTO, info->protocol != nexthdr)) |
71 | return false; |
72 | if (!(info->bitmask & (EBT_IP6_DPORT | |
73 | EBT_IP6_SPORT | EBT_IP6_ICMP6))) |
74 | return true; |
75 | |
76 | /* min icmpv6 headersize is 4, so sizeof(_pkthdr) is ok. */ |
77 | pptr = skb_header_pointer(skb, offset: offset_ph, len: sizeof(_pkthdr), |
78 | buffer: &_pkthdr); |
79 | if (pptr == NULL) |
80 | return false; |
81 | if (info->bitmask & EBT_IP6_DPORT) { |
82 | u16 dst = ntohs(pptr->tcpudphdr.dst); |
83 | if (NF_INVF(info, EBT_IP6_DPORT, |
84 | dst < info->dport[0] || |
85 | dst > info->dport[1])) |
86 | return false; |
87 | } |
88 | if (info->bitmask & EBT_IP6_SPORT) { |
89 | u16 src = ntohs(pptr->tcpudphdr.src); |
90 | if (NF_INVF(info, EBT_IP6_SPORT, |
91 | src < info->sport[0] || |
92 | src > info->sport[1])) |
93 | return false; |
94 | } |
95 | if ((info->bitmask & EBT_IP6_ICMP6) && |
96 | NF_INVF(info, EBT_IP6_ICMP6, |
97 | pptr->icmphdr.type < info->icmpv6_type[0] || |
98 | pptr->icmphdr.type > info->icmpv6_type[1] || |
99 | pptr->icmphdr.code < info->icmpv6_code[0] || |
100 | pptr->icmphdr.code > info->icmpv6_code[1])) |
101 | return false; |
102 | } |
103 | return true; |
104 | } |
105 | |
106 | static int ebt_ip6_mt_check(const struct xt_mtchk_param *par) |
107 | { |
108 | const struct ebt_entry *e = par->entryinfo; |
109 | struct ebt_ip6_info *info = par->matchinfo; |
110 | |
111 | if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) |
112 | return -EINVAL; |
113 | if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK) |
114 | return -EINVAL; |
115 | if (info->bitmask & (EBT_IP6_DPORT | EBT_IP6_SPORT)) { |
116 | if (info->invflags & EBT_IP6_PROTO) |
117 | return -EINVAL; |
118 | if (info->protocol != IPPROTO_TCP && |
119 | info->protocol != IPPROTO_UDP && |
120 | info->protocol != IPPROTO_UDPLITE && |
121 | info->protocol != IPPROTO_SCTP && |
122 | info->protocol != IPPROTO_DCCP) |
123 | return -EINVAL; |
124 | } |
125 | if (info->bitmask & EBT_IP6_DPORT && info->dport[0] > info->dport[1]) |
126 | return -EINVAL; |
127 | if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1]) |
128 | return -EINVAL; |
129 | if (info->bitmask & EBT_IP6_ICMP6) { |
130 | if ((info->invflags & EBT_IP6_PROTO) || |
131 | info->protocol != IPPROTO_ICMPV6) |
132 | return -EINVAL; |
133 | if (info->icmpv6_type[0] > info->icmpv6_type[1] || |
134 | info->icmpv6_code[0] > info->icmpv6_code[1]) |
135 | return -EINVAL; |
136 | } |
137 | return 0; |
138 | } |
139 | |
140 | static struct xt_match ebt_ip6_mt_reg __read_mostly = { |
141 | .name = "ip6" , |
142 | .revision = 0, |
143 | .family = NFPROTO_BRIDGE, |
144 | .match = ebt_ip6_mt, |
145 | .checkentry = ebt_ip6_mt_check, |
146 | .matchsize = sizeof(struct ebt_ip6_info), |
147 | .me = THIS_MODULE, |
148 | }; |
149 | |
150 | static int __init ebt_ip6_init(void) |
151 | { |
152 | return xt_register_match(target: &ebt_ip6_mt_reg); |
153 | } |
154 | |
155 | static void __exit ebt_ip6_fini(void) |
156 | { |
157 | xt_unregister_match(target: &ebt_ip6_mt_reg); |
158 | } |
159 | |
160 | module_init(ebt_ip6_init); |
161 | module_exit(ebt_ip6_fini); |
162 | MODULE_DESCRIPTION("Ebtables: IPv6 protocol packet match" ); |
163 | MODULE_AUTHOR("Kuo-Lang Tseng <kuo-lang.tseng@intel.com>" ); |
164 | MODULE_LICENSE("GPL" ); |
165 | |