1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ebt_ip |
4 | * |
5 | * Authors: |
6 | * Bart De Schuymer <bdschuym@pandora.be> |
7 | * |
8 | * April, 2002 |
9 | * |
10 | * Changes: |
11 | * added ip-sport and ip-dport |
12 | * Innominate Security Technologies AG <mhopf@innominate.com> |
13 | * September, 2002 |
14 | */ |
15 | #include <linux/ip.h> |
16 | #include <net/ip.h> |
17 | #include <linux/in.h> |
18 | #include <linux/module.h> |
19 | #include <linux/netfilter/x_tables.h> |
20 | #include <linux/netfilter_bridge/ebtables.h> |
21 | #include <linux/netfilter_bridge/ebt_ip.h> |
22 | |
23 | union pkthdr { |
24 | struct { |
25 | __be16 src; |
26 | __be16 dst; |
27 | } tcpudphdr; |
28 | struct { |
29 | u8 type; |
30 | u8 code; |
31 | } icmphdr; |
32 | struct { |
33 | u8 type; |
34 | } igmphdr; |
35 | }; |
36 | |
37 | static bool |
38 | ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) |
39 | { |
40 | const struct ebt_ip_info *info = par->matchinfo; |
41 | const struct iphdr *ih; |
42 | struct iphdr _iph; |
43 | const union pkthdr *pptr; |
44 | union pkthdr _pkthdr; |
45 | |
46 | ih = skb_header_pointer(skb, offset: 0, len: sizeof(_iph), buffer: &_iph); |
47 | if (ih == NULL) |
48 | return false; |
49 | if ((info->bitmask & EBT_IP_TOS) && |
50 | NF_INVF(info, EBT_IP_TOS, info->tos != ih->tos)) |
51 | return false; |
52 | if ((info->bitmask & EBT_IP_SOURCE) && |
53 | NF_INVF(info, EBT_IP_SOURCE, |
54 | (ih->saddr & info->smsk) != info->saddr)) |
55 | return false; |
56 | if ((info->bitmask & EBT_IP_DEST) && |
57 | NF_INVF(info, EBT_IP_DEST, |
58 | (ih->daddr & info->dmsk) != info->daddr)) |
59 | return false; |
60 | if (info->bitmask & EBT_IP_PROTO) { |
61 | if (NF_INVF(info, EBT_IP_PROTO, info->protocol != ih->protocol)) |
62 | return false; |
63 | if (!(info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT | |
64 | EBT_IP_ICMP | EBT_IP_IGMP))) |
65 | return true; |
66 | if (ntohs(ih->frag_off) & IP_OFFSET) |
67 | return false; |
68 | |
69 | /* min icmp/igmp headersize is 4, so sizeof(_pkthdr) is ok. */ |
70 | pptr = skb_header_pointer(skb, offset: ih->ihl*4, |
71 | len: sizeof(_pkthdr), buffer: &_pkthdr); |
72 | if (pptr == NULL) |
73 | return false; |
74 | if (info->bitmask & EBT_IP_DPORT) { |
75 | u32 dst = ntohs(pptr->tcpudphdr.dst); |
76 | if (NF_INVF(info, EBT_IP_DPORT, |
77 | dst < info->dport[0] || |
78 | dst > info->dport[1])) |
79 | return false; |
80 | } |
81 | if (info->bitmask & EBT_IP_SPORT) { |
82 | u32 src = ntohs(pptr->tcpudphdr.src); |
83 | if (NF_INVF(info, EBT_IP_SPORT, |
84 | src < info->sport[0] || |
85 | src > info->sport[1])) |
86 | return false; |
87 | } |
88 | if ((info->bitmask & EBT_IP_ICMP) && |
89 | NF_INVF(info, EBT_IP_ICMP, |
90 | pptr->icmphdr.type < info->icmp_type[0] || |
91 | pptr->icmphdr.type > info->icmp_type[1] || |
92 | pptr->icmphdr.code < info->icmp_code[0] || |
93 | pptr->icmphdr.code > info->icmp_code[1])) |
94 | return false; |
95 | if ((info->bitmask & EBT_IP_IGMP) && |
96 | NF_INVF(info, EBT_IP_IGMP, |
97 | pptr->igmphdr.type < info->igmp_type[0] || |
98 | pptr->igmphdr.type > info->igmp_type[1])) |
99 | return false; |
100 | } |
101 | return true; |
102 | } |
103 | |
104 | static int ebt_ip_mt_check(const struct xt_mtchk_param *par) |
105 | { |
106 | const struct ebt_ip_info *info = par->matchinfo; |
107 | const struct ebt_entry *e = par->entryinfo; |
108 | |
109 | if (e->ethproto != htons(ETH_P_IP) || |
110 | e->invflags & EBT_IPROTO) |
111 | return -EINVAL; |
112 | if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK) |
113 | return -EINVAL; |
114 | if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) { |
115 | if (info->invflags & EBT_IP_PROTO) |
116 | return -EINVAL; |
117 | if (info->protocol != IPPROTO_TCP && |
118 | info->protocol != IPPROTO_UDP && |
119 | info->protocol != IPPROTO_UDPLITE && |
120 | info->protocol != IPPROTO_SCTP && |
121 | info->protocol != IPPROTO_DCCP) |
122 | return -EINVAL; |
123 | } |
124 | if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1]) |
125 | return -EINVAL; |
126 | if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1]) |
127 | return -EINVAL; |
128 | if (info->bitmask & EBT_IP_ICMP) { |
129 | if ((info->invflags & EBT_IP_PROTO) || |
130 | info->protocol != IPPROTO_ICMP) |
131 | return -EINVAL; |
132 | if (info->icmp_type[0] > info->icmp_type[1] || |
133 | info->icmp_code[0] > info->icmp_code[1]) |
134 | return -EINVAL; |
135 | } |
136 | if (info->bitmask & EBT_IP_IGMP) { |
137 | if ((info->invflags & EBT_IP_PROTO) || |
138 | info->protocol != IPPROTO_IGMP) |
139 | return -EINVAL; |
140 | if (info->igmp_type[0] > info->igmp_type[1]) |
141 | return -EINVAL; |
142 | } |
143 | return 0; |
144 | } |
145 | |
146 | static struct xt_match ebt_ip_mt_reg __read_mostly = { |
147 | .name = "ip" , |
148 | .revision = 0, |
149 | .family = NFPROTO_BRIDGE, |
150 | .match = ebt_ip_mt, |
151 | .checkentry = ebt_ip_mt_check, |
152 | .matchsize = sizeof(struct ebt_ip_info), |
153 | .me = THIS_MODULE, |
154 | }; |
155 | |
156 | static int __init ebt_ip_init(void) |
157 | { |
158 | return xt_register_match(target: &ebt_ip_mt_reg); |
159 | } |
160 | |
161 | static void __exit ebt_ip_fini(void) |
162 | { |
163 | xt_unregister_match(target: &ebt_ip_mt_reg); |
164 | } |
165 | |
166 | module_init(ebt_ip_init); |
167 | module_exit(ebt_ip_fini); |
168 | MODULE_DESCRIPTION("Ebtables: IPv4 protocol packet match" ); |
169 | MODULE_LICENSE("GPL" ); |
170 | |