| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * net/sched/em_ipt.c IPtables matches Ematch |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * (c) 2018 Eyal Birger <eyal.birger@gmail.com> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/gfp.h> |
|---|
| .. | .. |
|---|
| 25 | 21 | struct em_ipt_match { |
|---|
| 26 | 22 | const struct xt_match *match; |
|---|
| 27 | 23 | u32 hook; |
|---|
| 28 | | - u8 match_data[0] __aligned(8); |
|---|
| 24 | + u8 nfproto; |
|---|
| 25 | + u8 match_data[] __aligned(8); |
|---|
| 29 | 26 | }; |
|---|
| 30 | 27 | |
|---|
| 31 | 28 | struct em_ipt_xt_match { |
|---|
| .. | .. |
|---|
| 75 | 72 | return 0; |
|---|
| 76 | 73 | } |
|---|
| 77 | 74 | |
|---|
| 75 | +static int addrtype_validate_match_data(struct nlattr **tb, u8 mrev) |
|---|
| 76 | +{ |
|---|
| 77 | + if (mrev != 1) { |
|---|
| 78 | + pr_err("only addrtype match revision 1 supported"); |
|---|
| 79 | + return -EINVAL; |
|---|
| 80 | + } |
|---|
| 81 | + |
|---|
| 82 | + return 0; |
|---|
| 83 | +} |
|---|
| 84 | + |
|---|
| 78 | 85 | static const struct em_ipt_xt_match em_ipt_xt_matches[] = { |
|---|
| 79 | 86 | { |
|---|
| 80 | 87 | .match_name = "policy", |
|---|
| 81 | 88 | .validate_match_data = policy_validate_match_data |
|---|
| 89 | + }, |
|---|
| 90 | + { |
|---|
| 91 | + .match_name = "addrtype", |
|---|
| 92 | + .validate_match_data = addrtype_validate_match_data |
|---|
| 82 | 93 | }, |
|---|
| 83 | 94 | {} |
|---|
| 84 | 95 | }; |
|---|
| .. | .. |
|---|
| 119 | 130 | struct em_ipt_match *im = NULL; |
|---|
| 120 | 131 | struct xt_match *match; |
|---|
| 121 | 132 | int mdata_len, ret; |
|---|
| 133 | + u8 nfproto; |
|---|
| 122 | 134 | |
|---|
| 123 | | - ret = nla_parse(tb, TCA_EM_IPT_MAX, data, data_len, em_ipt_policy, |
|---|
| 124 | | - NULL); |
|---|
| 135 | + ret = nla_parse_deprecated(tb, TCA_EM_IPT_MAX, data, data_len, |
|---|
| 136 | + em_ipt_policy, NULL); |
|---|
| 125 | 137 | if (ret < 0) |
|---|
| 126 | 138 | return ret; |
|---|
| 127 | 139 | |
|---|
| 128 | 140 | if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] || |
|---|
| 129 | 141 | !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO]) |
|---|
| 130 | 142 | return -EINVAL; |
|---|
| 143 | + |
|---|
| 144 | + nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]); |
|---|
| 145 | + switch (nfproto) { |
|---|
| 146 | + case NFPROTO_IPV4: |
|---|
| 147 | + case NFPROTO_IPV6: |
|---|
| 148 | + break; |
|---|
| 149 | + default: |
|---|
| 150 | + return -EINVAL; |
|---|
| 151 | + } |
|---|
| 131 | 152 | |
|---|
| 132 | 153 | match = get_xt_match(tb); |
|---|
| 133 | 154 | if (IS_ERR(match)) { |
|---|
| .. | .. |
|---|
| 144 | 165 | |
|---|
| 145 | 166 | im->match = match; |
|---|
| 146 | 167 | im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]); |
|---|
| 168 | + im->nfproto = nfproto; |
|---|
| 147 | 169 | nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len); |
|---|
| 148 | 170 | |
|---|
| 149 | 171 | ret = check_match(net, im, mdata_len); |
|---|
| .. | .. |
|---|
| 177 | 199 | im->match->destroy(&par); |
|---|
| 178 | 200 | } |
|---|
| 179 | 201 | module_put(im->match->me); |
|---|
| 180 | | - kfree((void *)im); |
|---|
| 202 | + kfree(im); |
|---|
| 181 | 203 | } |
|---|
| 182 | 204 | |
|---|
| 183 | 205 | static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em, |
|---|
| .. | .. |
|---|
| 186 | 208 | const struct em_ipt_match *im = (const void *)em->data; |
|---|
| 187 | 209 | struct xt_action_param acpar = {}; |
|---|
| 188 | 210 | struct net_device *indev = NULL; |
|---|
| 211 | + u8 nfproto = im->match->family; |
|---|
| 189 | 212 | struct nf_hook_state state; |
|---|
| 190 | 213 | int ret; |
|---|
| 214 | + |
|---|
| 215 | + switch (skb_protocol(skb, true)) { |
|---|
| 216 | + case htons(ETH_P_IP): |
|---|
| 217 | + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) |
|---|
| 218 | + return 0; |
|---|
| 219 | + if (nfproto == NFPROTO_UNSPEC) |
|---|
| 220 | + nfproto = NFPROTO_IPV4; |
|---|
| 221 | + break; |
|---|
| 222 | + case htons(ETH_P_IPV6): |
|---|
| 223 | + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) |
|---|
| 224 | + return 0; |
|---|
| 225 | + if (nfproto == NFPROTO_UNSPEC) |
|---|
| 226 | + nfproto = NFPROTO_IPV6; |
|---|
| 227 | + break; |
|---|
| 228 | + default: |
|---|
| 229 | + return 0; |
|---|
| 230 | + } |
|---|
| 191 | 231 | |
|---|
| 192 | 232 | rcu_read_lock(); |
|---|
| 193 | 233 | |
|---|
| 194 | 234 | if (skb->skb_iif) |
|---|
| 195 | 235 | indev = dev_get_by_index_rcu(em->net, skb->skb_iif); |
|---|
| 196 | 236 | |
|---|
| 197 | | - nf_hook_state_init(&state, im->hook, im->match->family, |
|---|
| 237 | + nf_hook_state_init(&state, im->hook, nfproto, |
|---|
| 198 | 238 | indev ?: skb->dev, skb->dev, NULL, em->net, NULL); |
|---|
| 199 | 239 | |
|---|
| 200 | 240 | acpar.match = im->match; |
|---|
| .. | .. |
|---|
| 217 | 257 | return -EMSGSIZE; |
|---|
| 218 | 258 | if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0) |
|---|
| 219 | 259 | return -EMSGSIZE; |
|---|
| 220 | | - if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->match->family) < 0) |
|---|
| 260 | + if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->nfproto) < 0) |
|---|
| 221 | 261 | return -EMSGSIZE; |
|---|
| 222 | 262 | if (nla_put(skb, TCA_EM_IPT_MATCH_DATA, |
|---|
| 223 | 263 | im->match->usersize ?: im->match->matchsize, |
|---|