| 1 | |
| 2 | |
| 3 | * Copyright (c) 2008-2009 Patrick McHardy <[email protected]>
|
| 4 | * Copyright (c) 2014 Intel Corporation
|
| 5 | * Author: Tomasz Bursztyka <[email protected]>
|
| 6 | *
|
| 7 | * Development of this code funded by Astaro AG (http://www.astaro.com/)
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/kernel.h>
|
| 11 | #include <linux/netlink.h>
|
| 12 | #include <linux/netfilter.h>
|
| 13 | #include <linux/netfilter/nf_tables.h>
|
| 14 | #include <linux/in.h>
|
| 15 | #include <linux/ip.h>
|
| 16 | #include <linux/ipv6.h>
|
| 17 | #include <linux/random.h>
|
| 18 | #include <linux/smp.h>
|
| 19 | #include <linux/static_key.h>
|
| 20 | #include <net/dst.h>
|
| 21 | #include <net/ip.h>
|
| 22 | #include <net/sock.h>
|
| 23 | #include <net/dst_metadata.h>
|
| 24 | #include <net/tcp_states.h>
|
| 25 | #include <net/netfilter/nf_tables.h>
|
| 26 | #include <net/netfilter/nf_tables_core.h>
|
| 27 | #include <net/netfilter/nf_tables_ipv4.h>
|
| 28 | #include <net/netfilter/nf_tables_ipv6.h>
|
| 29 | #include <net/netfilter/nft_meta.h>
|
| 30 | #include <net/netfilter/nf_tables_offload.h>
|
| 31 |
|
| 32 | #include <uapi/linux/netfilter_bridge.h>
|
| 33 |
|
| 34 | #define NFT_META_SECS_PER_MINUTE 60
|
| 35 | #define NFT_META_SECS_PER_HOUR 3600
|
| 36 | #define NFT_META_SECS_PER_DAY 86400
|
| 37 | #define NFT_META_DAYS_PER_WEEK 7
|
| 38 |
|
| 39 | static u8 nft_meta_weekday(void)
|
| 40 | {
|
| 41 | time64_t secs = ktime_get_real_seconds();
|
| 42 | unsigned int dse;
|
| 43 | u8 wday;
|
| 44 |
|
| 45 | secs -= NFT_META_SECS_PER_MINUTE * sys_tz.tz_minuteswest;
|
| 46 | dse = div_u64(secs, NFT_META_SECS_PER_DAY);
|
| 47 | wday = (4 + dse) % NFT_META_DAYS_PER_WEEK;
|
| 48 |
|
| 49 | return wday;
|
| 50 | }
|
| 51 |
|
| 52 | static u32 nft_meta_hour(time64_t secs)
|
| 53 | {
|
| 54 | struct tm tm;
|
| 55 |
|
| 56 | time64_to_tm(secs, 0, &tm);
|
| 57 |
|
| 58 | return tm.tm_hour * NFT_META_SECS_PER_HOUR
|
| 59 | + tm.tm_min * NFT_META_SECS_PER_MINUTE
|
| 60 | + tm.tm_sec;
|
| 61 | }
|
| 62 |
|
| 63 | static noinline_for_stack void
|
| 64 | nft_meta_get_eval_time(enum nft_meta_keys key,
|
| 65 | u32 *dest)
|
| 66 | {
|
| 67 | switch (key) {
|
| 68 | case NFT_META_TIME_NS:
|
| 69 | nft_reg_store64((u64 *)dest, ktime_get_real_ns());
|
| 70 | break;
|
| 71 | case NFT_META_TIME_DAY:
|
| 72 | nft_reg_store8(dest, nft_meta_weekday());
|
| 73 | break;
|
| 74 | case NFT_META_TIME_HOUR:
|
| 75 | *dest = nft_meta_hour(ktime_get_real_seconds());
|
| 76 | break;
|
| 77 | default:
|
| 78 | break;
|
| 79 | }
|
| 80 | }
|
| 81 |
|
| 82 | static noinline bool
|
| 83 | nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt,
|
| 84 | u32 *dest)
|
| 85 | {
|
| 86 | const struct sk_buff *skb = pkt->skb;
|
| 87 |
|
| 88 | switch (nft_pf(pkt)) {
|
| 89 | case NFPROTO_IPV4:
|
| 90 | if (ipv4_is_multicast(ip_hdr(skb)->daddr))
|
| 91 | nft_reg_store8(dest, PACKET_MULTICAST);
|
| 92 | else
|
| 93 | nft_reg_store8(dest, PACKET_BROADCAST);
|
| 94 | break;
|
| 95 | case NFPROTO_IPV6:
|
| 96 | nft_reg_store8(dest, PACKET_MULTICAST);
|
| 97 | break;
|
| 98 | case NFPROTO_NETDEV:
|
| 99 | switch (skb->protocol) {
|
| 100 | case htons(ETH_P_IP): {
|
| 101 | int noff = skb_network_offset(skb);
|
| 102 | struct iphdr *iph, _iph;
|
| 103 |
|
| 104 | iph = skb_header_pointer(skb, noff,
|
| 105 | sizeof(_iph), &_iph);
|
| 106 | if (!iph)
|
| 107 | return false;
|
| 108 |
|
| 109 | if (ipv4_is_multicast(iph->daddr))
|
| 110 | nft_reg_store8(dest, PACKET_MULTICAST);
|
| 111 | else
|
| 112 | nft_reg_store8(dest, PACKET_BROADCAST);
|
| 113 |
|
| 114 | break;
|
| 115 | }
|
| 116 | case htons(ETH_P_IPV6):
|
| 117 | nft_reg_store8(dest, PACKET_MULTICAST);
|
| 118 | break;
|
| 119 | default:
|
| 120 | DEBUG_NET_WARN_ON_ONCE(1);
|
| 121 | return false;
|
| 122 | }
|
| 123 | break;
|
| 124 | default:
|
| 125 | DEBUG_NET_WARN_ON_ONCE(1);
|
| 126 | return false;
|
| 127 | }
|
| 128 |
|
| 129 | return true;
|
| 130 | }
|
| 131 |
|
| 132 | static noinline bool
|
| 133 | nft_meta_get_eval_skugid(enum nft_meta_keys key,
|
| 134 | u32 *dest,
|
| 135 | const struct nft_pktinfo *pkt)
|
| 136 | {
|
| 137 | const struct sock *sk = skb_to_full_sk(pkt->skb);
|
| 138 | const struct socket *sock;
|
| 139 | const struct file *file;
|
| 140 |
|
| 141 | if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk)))
|
| 142 | return false;
|
| 143 |
|
| 144 | |
| 145 | * file pointer may become NULL if the socket is closed. Both structures
|
| 146 | * (including file->cred) are RCU freed which means they can be accessed
|
| 147 | * within a RCU read section.
|
| 148 |
|
| 149 | sock = READ_ONCE(sk->sk_socket);
|
| 150 | file = sock ? READ_ONCE(sock->file) : NULL;
|
| 151 | if (!file)
|
| 152 | return false;
|
| 153 |
|
| 154 | switch (key) {
|
| 155 | case NFT_META_SKUID:
|
| 156 | *dest = from_kuid_munged(sock_net(sk)->user_ns,
|
| 157 | file->f_cred->fsuid);
|
| 158 | break;
|
| 159 | case NFT_META_SKGID:
|
| 160 | *dest = from_kgid_munged(sock_net(sk)->user_ns,
|
| 161 | file->f_cred->fsgid);
|
| 162 | break;
|
| 163 | default:
|
| 164 | break;
|
| 165 | }
|
| 166 |
|
| 167 | return true;
|
| 168 | }
|
| 169 |
|
| 170 | #ifdef CONFIG_CGROUP_NET_CLASSID
|
| 171 | static noinline bool
|
| 172 | nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt)
|
| 173 | {
|
| 174 | struct sock *sk = skb_to_full_sk(pkt->skb);
|
| 175 |
|
| 176 | if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk)))
|
| 177 | return false;
|
| 178 |
|
| 179 | *dest = sock_cgroup_classid(&sk->sk_cgrp_data);
|
| 180 | return true;
|
| 181 | }
|
| 182 | #endif
|
| 183 |
|
| 184 | static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key,
|
| 185 | u32 *dest,
|
| 186 | const struct nft_pktinfo *pkt)
|
| 187 | {
|
| 188 | const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
|
| 189 |
|
| 190 | switch (key) {
|
| 191 | case NFT_META_IIFKIND:
|
| 192 | if (!in || !in->rtnl_link_ops)
|
| 193 | return false;
|
| 194 | strscpy_pad((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ);
|
| 195 | break;
|
| 196 | case NFT_META_OIFKIND:
|
| 197 | if (!out || !out->rtnl_link_ops)
|
| 198 | return false;
|
| 199 | strscpy_pad((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ);
|
| 200 | break;
|
| 201 | default:
|
| 202 | return false;
|
| 203 | }
|
| 204 |
|
| 205 | return true;
|
| 206 | }
|
| 207 |
|
| 208 | static void nft_meta_store_ifindex(u32 *dest, const struct net_device *dev)
|
| 209 | {
|
| 210 | *dest = dev ? dev->ifindex : 0;
|
| 211 | }
|
| 212 |
|
| 213 | static void nft_meta_store_ifname(u32 *dest, const struct net_device *dev)
|
| 214 | {
|
| 215 | strscpy_pad((char *)dest, dev ? dev->name : "", IFNAMSIZ);
|
| 216 | }
|
| 217 |
|
| 218 | static bool nft_meta_store_iftype(u32 *dest, const struct net_device *dev)
|
| 219 | {
|
| 220 | if (!dev)
|
| 221 | return false;
|
| 222 |
|
| 223 | nft_reg_store16(dest, dev->type);
|
| 224 | return true;
|
| 225 | }
|
| 226 |
|
| 227 | static bool nft_meta_store_ifgroup(u32 *dest, const struct net_device *dev)
|
| 228 | {
|
| 229 | if (!dev)
|
| 230 | return false;
|
| 231 |
|
| 232 | *dest = dev->group;
|
| 233 | return true;
|
| 234 | }
|
| 235 |
|
| 236 | static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
|
| 237 | const struct nft_pktinfo *pkt)
|
| 238 | {
|
| 239 | switch (key) {
|
| 240 | case NFT_META_IIFNAME:
|
| 241 | nft_meta_store_ifname(dest, nft_in(pkt));
|
| 242 | break;
|
| 243 | case NFT_META_OIFNAME:
|
| 244 | nft_meta_store_ifname(dest, nft_out(pkt));
|
| 245 | break;
|
| 246 | case NFT_META_IIF:
|
| 247 | nft_meta_store_ifindex(dest, nft_in(pkt));
|
| 248 | break;
|
| 249 | case NFT_META_OIF:
|
| 250 | nft_meta_store_ifindex(dest, nft_out(pkt));
|
| 251 | break;
|
| 252 | case NFT_META_IFTYPE:
|
| 253 | if (!nft_meta_store_iftype(dest, pkt->skb->dev))
|
| 254 | return false;
|
| 255 | break;
|
| 256 | case __NFT_META_IIFTYPE:
|
| 257 | if (!nft_meta_store_iftype(dest, nft_in(pkt)))
|
| 258 | return false;
|
| 259 | break;
|
| 260 | case NFT_META_OIFTYPE:
|
| 261 | if (!nft_meta_store_iftype(dest, nft_out(pkt)))
|
| 262 | return false;
|
| 263 | break;
|
| 264 | case NFT_META_IIFGROUP:
|
| 265 | if (!nft_meta_store_ifgroup(dest, nft_in(pkt)))
|
| 266 | return false;
|
| 267 | break;
|
| 268 | case NFT_META_OIFGROUP:
|
| 269 | if (!nft_meta_store_ifgroup(dest, nft_out(pkt)))
|
| 270 | return false;
|
| 271 | break;
|
| 272 | default:
|
| 273 | return false;
|
| 274 | }
|
| 275 |
|
| 276 | return true;
|
| 277 | }
|
| 278 |
|
| 279 | #ifdef CONFIG_IP_ROUTE_CLASSID
|
| 280 | static noinline bool
|
| 281 | nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest)
|
| 282 | {
|
| 283 | const struct dst_entry *dst;
|
| 284 |
|
| 285 | if (!skb_valid_dst(skb))
|
| 286 | return false;
|
| 287 |
|
| 288 | dst = skb_dst(skb);
|
| 289 | *dest = dst->tclassid;
|
| 290 | return true;
|
| 291 | }
|
| 292 | #endif
|
| 293 |
|
| 294 | static noinline u32 nft_meta_get_eval_sdif(const struct nft_pktinfo *pkt)
|
| 295 | {
|
| 296 | switch (nft_pf(pkt)) {
|
| 297 | case NFPROTO_IPV4:
|
| 298 | return inet_sdif(pkt->skb);
|
| 299 | case NFPROTO_IPV6:
|
| 300 | return inet6_sdif(pkt->skb);
|
| 301 | }
|
| 302 |
|
| 303 | return 0;
|
| 304 | }
|
| 305 |
|
| 306 | static noinline void
|
| 307 | nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt)
|
| 308 | {
|
| 309 | u32 sdif = nft_meta_get_eval_sdif(pkt);
|
| 310 | const struct net_device *dev;
|
| 311 |
|
| 312 | dev = sdif ? dev_get_by_index_rcu(nft_net(pkt), sdif) : NULL;
|
| 313 | nft_meta_store_ifname(dest, dev);
|
| 314 | }
|
| 315 |
|
| 316 | static void nft_meta_pktinfo_may_update(struct nft_pktinfo *pkt)
|
| 317 | {
|
| 318 | struct sk_buff *skb = pkt->skb;
|
| 319 | struct vlan_ethhdr *veth;
|
| 320 | __be16 ethertype;
|
| 321 | int nhoff;
|
| 322 |
|
| 323 |
|
| 324 | if (pkt->flags)
|
| 325 | return;
|
| 326 |
|
| 327 |
|
| 328 | switch (skb->protocol) {
|
| 329 | case htons(ETH_P_8021Q):
|
| 330 | if (!pskb_may_pull(skb, skb_mac_offset(skb) + sizeof(*veth)))
|
| 331 | return;
|
| 332 |
|
| 333 | veth = (struct vlan_ethhdr *)skb_mac_header(skb);
|
| 334 | nhoff = VLAN_HLEN;
|
| 335 | ethertype = veth->h_vlan_encapsulated_proto;
|
| 336 | break;
|
| 337 | case htons(ETH_P_PPP_SES):
|
| 338 | if (!nf_flow_pppoe_proto(skb, ðertype))
|
| 339 | return;
|
| 340 |
|
| 341 | nhoff = PPPOE_SES_HLEN;
|
| 342 | break;
|
| 343 | default:
|
| 344 | return;
|
| 345 | }
|
| 346 |
|
| 347 | nhoff += skb_network_offset(skb);
|
| 348 | switch (ethertype) {
|
| 349 | case htons(ETH_P_IP):
|
| 350 | if (__nft_set_pktinfo_ipv4_validate(pkt, nhoff))
|
| 351 | nft_set_pktinfo_unspec(pkt);
|
| 352 | break;
|
| 353 | case htons(ETH_P_IPV6):
|
| 354 | if (__nft_set_pktinfo_ipv6_validate(pkt, nhoff))
|
| 355 | nft_set_pktinfo_unspec(pkt);
|
| 356 | break;
|
| 357 | default:
|
| 358 | break;
|
| 359 | }
|
| 360 |
|
| 361 | pkt->ethertype = ethertype;
|
| 362 | }
|
| 363 |
|
| 364 | void nft_meta_get_eval(const struct nft_expr *expr,
|
| 365 | struct nft_regs *regs,
|
| 366 | const struct nft_pktinfo *pkt)
|
| 367 | {
|
| 368 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 369 | const struct sk_buff *skb = pkt->skb;
|
| 370 | u32 *dest = ®s->data[priv->dreg];
|
| 371 |
|
| 372 | switch (priv->key) {
|
| 373 | case NFT_META_LEN:
|
| 374 | *dest = skb->len;
|
| 375 | break;
|
| 376 | case NFT_META_PROTOCOL:
|
| 377 | nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);
|
| 378 | nft_reg_store16(dest, (__force u16)pkt->ethertype);
|
| 379 | break;
|
| 380 | case NFT_META_NFPROTO:
|
| 381 | nft_reg_store8(dest, nft_pf(pkt));
|
| 382 | break;
|
| 383 | case NFT_META_L4PROTO:
|
| 384 | nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);
|
| 385 | if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
|
| 386 | goto err;
|
| 387 | nft_reg_store8(dest, pkt->tprot);
|
| 388 | break;
|
| 389 | case NFT_META_PRIORITY:
|
| 390 | *dest = skb->priority;
|
| 391 | break;
|
| 392 | case NFT_META_MARK:
|
| 393 | *dest = skb->mark;
|
| 394 | break;
|
| 395 | case NFT_META_IIF:
|
| 396 | case NFT_META_OIF:
|
| 397 | case NFT_META_IIFNAME:
|
| 398 | case NFT_META_OIFNAME:
|
| 399 | case NFT_META_IIFTYPE:
|
| 400 | case NFT_META_OIFTYPE:
|
| 401 | case NFT_META_IIFGROUP:
|
| 402 | case NFT_META_OIFGROUP:
|
| 403 | if (!nft_meta_get_eval_ifname(priv->key, dest, pkt))
|
| 404 | goto err;
|
| 405 | break;
|
| 406 | case NFT_META_SKUID:
|
| 407 | case NFT_META_SKGID:
|
| 408 | if (!nft_meta_get_eval_skugid(priv->key, dest, pkt))
|
| 409 | goto err;
|
| 410 | break;
|
| 411 | #ifdef CONFIG_IP_ROUTE_CLASSID
|
| 412 | case NFT_META_RTCLASSID:
|
| 413 | if (!nft_meta_get_eval_rtclassid(skb, dest))
|
| 414 | goto err;
|
| 415 | break;
|
| 416 | #endif
|
| 417 | #ifdef CONFIG_NETWORK_SECMARK
|
| 418 | case NFT_META_SECMARK:
|
| 419 | *dest = skb->secmark;
|
| 420 | break;
|
| 421 | #endif
|
| 422 | case NFT_META_PKTTYPE:
|
| 423 | if (skb->pkt_type != PACKET_LOOPBACK) {
|
| 424 | nft_reg_store8(dest, skb->pkt_type);
|
| 425 | break;
|
| 426 | }
|
| 427 |
|
| 428 | if (!nft_meta_get_eval_pkttype_lo(pkt, dest))
|
| 429 | goto err;
|
| 430 | break;
|
| 431 | case NFT_META_CPU:
|
| 432 | *dest = raw_smp_processor_id();
|
| 433 | break;
|
| 434 | #ifdef CONFIG_CGROUP_NET_CLASSID
|
| 435 | case NFT_META_CGROUP:
|
| 436 | if (!nft_meta_get_eval_cgroup(dest, pkt))
|
| 437 | goto err;
|
| 438 | break;
|
| 439 | #endif
|
| 440 | case NFT_META_PRANDOM:
|
| 441 | *dest = get_random_u32();
|
| 442 | break;
|
| 443 | #ifdef CONFIG_XFRM
|
| 444 | case NFT_META_SECPATH:
|
| 445 | nft_reg_store8(dest, secpath_exists(skb));
|
| 446 | break;
|
| 447 | #endif
|
| 448 | case NFT_META_IIFKIND:
|
| 449 | case NFT_META_OIFKIND:
|
| 450 | if (!nft_meta_get_eval_kind(priv->key, dest, pkt))
|
| 451 | goto err;
|
| 452 | break;
|
| 453 | case NFT_META_TIME_NS:
|
| 454 | case NFT_META_TIME_DAY:
|
| 455 | case NFT_META_TIME_HOUR:
|
| 456 | nft_meta_get_eval_time(priv->key, dest);
|
| 457 | break;
|
| 458 | case NFT_META_SDIF:
|
| 459 | *dest = nft_meta_get_eval_sdif(pkt);
|
| 460 | break;
|
| 461 | case NFT_META_SDIFNAME:
|
| 462 | nft_meta_get_eval_sdifname(dest, pkt);
|
| 463 | break;
|
| 464 | default:
|
| 465 | DEBUG_NET_WARN_ON_ONCE(1);
|
| 466 | goto err;
|
| 467 | }
|
| 468 | return;
|
| 469 |
|
| 470 | err:
|
| 471 | regs->verdict.code = NFT_BREAK;
|
| 472 | }
|
| 473 | EXPORT_SYMBOL_GPL(nft_meta_get_eval);
|
| 474 |
|
| 475 | void nft_meta_set_eval(const struct nft_expr *expr,
|
| 476 | struct nft_regs *regs,
|
| 477 | const struct nft_pktinfo *pkt)
|
| 478 | {
|
| 479 | const struct nft_meta *meta = nft_expr_priv(expr);
|
| 480 | struct sk_buff *skb = pkt->skb;
|
| 481 | u32 *sreg = ®s->data[meta->sreg];
|
| 482 | u32 value = *sreg;
|
| 483 | u8 value8;
|
| 484 |
|
| 485 | switch (meta->key) {
|
| 486 | case NFT_META_MARK:
|
| 487 | skb->mark = value;
|
| 488 | break;
|
| 489 | case NFT_META_PRIORITY:
|
| 490 | skb->priority = value;
|
| 491 | break;
|
| 492 | case NFT_META_PKTTYPE:
|
| 493 | value8 = nft_reg_load8(sreg);
|
| 494 |
|
| 495 | if (skb->pkt_type != value8 &&
|
| 496 | skb_pkt_type_ok(value8) &&
|
| 497 | skb_pkt_type_ok(skb->pkt_type))
|
| 498 | skb->pkt_type = value8;
|
| 499 | break;
|
| 500 | case NFT_META_NFTRACE:
|
| 501 | value8 = nft_reg_load8(sreg);
|
| 502 |
|
| 503 | skb->nf_trace = !!value8;
|
| 504 | break;
|
| 505 | #ifdef CONFIG_NETWORK_SECMARK
|
| 506 | case NFT_META_SECMARK:
|
| 507 | skb->secmark = value;
|
| 508 | break;
|
| 509 | #endif
|
| 510 | default:
|
| 511 | DEBUG_NET_WARN_ON_ONCE(1);
|
| 512 | }
|
| 513 | }
|
| 514 | EXPORT_SYMBOL_GPL(nft_meta_set_eval);
|
| 515 |
|
| 516 | const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
|
| 517 | [NFTA_META_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
|
| 518 | [NFTA_META_KEY] = NLA_POLICY_MAX(NLA_BE32, 255),
|
| 519 | [NFTA_META_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
|
| 520 | };
|
| 521 | EXPORT_SYMBOL_GPL(nft_meta_policy);
|
| 522 |
|
| 523 | int nft_meta_get_init(const struct nft_ctx *ctx,
|
| 524 | const struct nft_expr *expr,
|
| 525 | const struct nlattr * const tb[])
|
| 526 | {
|
| 527 | struct nft_meta *priv = nft_expr_priv(expr);
|
| 528 | unsigned int len;
|
| 529 |
|
| 530 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
|
| 531 | switch (priv->key) {
|
| 532 | case NFT_META_PROTOCOL:
|
| 533 | case NFT_META_IIFTYPE:
|
| 534 | case NFT_META_OIFTYPE:
|
| 535 | len = sizeof(u16);
|
| 536 | break;
|
| 537 | case NFT_META_NFPROTO:
|
| 538 | case NFT_META_L4PROTO:
|
| 539 | case NFT_META_LEN:
|
| 540 | case NFT_META_PRIORITY:
|
| 541 | case NFT_META_MARK:
|
| 542 | case NFT_META_IIF:
|
| 543 | case NFT_META_OIF:
|
| 544 | case NFT_META_SDIF:
|
| 545 | case NFT_META_SKUID:
|
| 546 | case NFT_META_SKGID:
|
| 547 | #ifdef CONFIG_IP_ROUTE_CLASSID
|
| 548 | case NFT_META_RTCLASSID:
|
| 549 | #endif
|
| 550 | #ifdef CONFIG_NETWORK_SECMARK
|
| 551 | case NFT_META_SECMARK:
|
| 552 | #endif
|
| 553 | case NFT_META_PKTTYPE:
|
| 554 | case NFT_META_CPU:
|
| 555 | case NFT_META_IIFGROUP:
|
| 556 | case NFT_META_OIFGROUP:
|
| 557 | #ifdef CONFIG_CGROUP_NET_CLASSID
|
| 558 | case NFT_META_CGROUP:
|
| 559 | #endif
|
| 560 | len = sizeof(u32);
|
| 561 | break;
|
| 562 | case NFT_META_IIFNAME:
|
| 563 | case NFT_META_OIFNAME:
|
| 564 | case NFT_META_IIFKIND:
|
| 565 | case NFT_META_OIFKIND:
|
| 566 | case NFT_META_SDIFNAME:
|
| 567 | len = IFNAMSIZ;
|
| 568 | break;
|
| 569 | case NFT_META_PRANDOM:
|
| 570 | len = sizeof(u32);
|
| 571 | break;
|
| 572 | #ifdef CONFIG_XFRM
|
| 573 | case NFT_META_SECPATH:
|
| 574 | len = sizeof(u8);
|
| 575 | break;
|
| 576 | #endif
|
| 577 | case NFT_META_TIME_NS:
|
| 578 | len = sizeof(u64);
|
| 579 | break;
|
| 580 | case NFT_META_TIME_DAY:
|
| 581 | len = sizeof(u8);
|
| 582 | break;
|
| 583 | case NFT_META_TIME_HOUR:
|
| 584 | len = sizeof(u32);
|
| 585 | break;
|
| 586 | default:
|
| 587 | return -EOPNOTSUPP;
|
| 588 | }
|
| 589 |
|
| 590 | priv->len = len;
|
| 591 | return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
|
| 592 | NULL, NFT_DATA_VALUE, len);
|
| 593 | }
|
| 594 | EXPORT_SYMBOL_GPL(nft_meta_get_init);
|
| 595 |
|
| 596 | static int nft_meta_get_validate_sdif(const struct nft_ctx *ctx)
|
| 597 | {
|
| 598 | unsigned int hooks;
|
| 599 |
|
| 600 | switch (ctx->family) {
|
| 601 | case NFPROTO_IPV4:
|
| 602 | case NFPROTO_IPV6:
|
| 603 | case NFPROTO_INET:
|
| 604 | hooks = (1 << NF_INET_LOCAL_IN) |
|
| 605 | (1 << NF_INET_FORWARD);
|
| 606 | break;
|
| 607 | default:
|
| 608 | return -EOPNOTSUPP;
|
| 609 | }
|
| 610 |
|
| 611 | return nft_chain_validate_hooks(ctx->chain, hooks);
|
| 612 | }
|
| 613 |
|
| 614 | static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx)
|
| 615 | {
|
| 616 | #ifdef CONFIG_XFRM
|
| 617 | unsigned int hooks;
|
| 618 |
|
| 619 | switch (ctx->family) {
|
| 620 | case NFPROTO_NETDEV:
|
| 621 | hooks = 1 << NF_NETDEV_INGRESS;
|
| 622 | break;
|
| 623 | case NFPROTO_IPV4:
|
| 624 | case NFPROTO_IPV6:
|
| 625 | case NFPROTO_INET:
|
| 626 | hooks = (1 << NF_INET_PRE_ROUTING) |
|
| 627 | (1 << NF_INET_LOCAL_IN) |
|
| 628 | (1 << NF_INET_FORWARD);
|
| 629 | break;
|
| 630 | default:
|
| 631 | return -EOPNOTSUPP;
|
| 632 | }
|
| 633 |
|
| 634 | return nft_chain_validate_hooks(ctx->chain, hooks);
|
| 635 | #else
|
| 636 | return 0;
|
| 637 | #endif
|
| 638 | }
|
| 639 |
|
| 640 | int nft_meta_get_validate(const struct nft_ctx *ctx,
|
| 641 | const struct nft_expr *expr)
|
| 642 | {
|
| 643 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 644 |
|
| 645 | switch (priv->key) {
|
| 646 | case NFT_META_SECPATH:
|
| 647 | return nft_meta_get_validate_xfrm(ctx);
|
| 648 | case NFT_META_SDIF:
|
| 649 | case NFT_META_SDIFNAME:
|
| 650 | return nft_meta_get_validate_sdif(ctx);
|
| 651 | default:
|
| 652 | break;
|
| 653 | }
|
| 654 |
|
| 655 | return 0;
|
| 656 | }
|
| 657 | EXPORT_SYMBOL_GPL(nft_meta_get_validate);
|
| 658 |
|
| 659 | int nft_meta_set_validate(const struct nft_ctx *ctx,
|
| 660 | const struct nft_expr *expr)
|
| 661 | {
|
| 662 | struct nft_meta *priv = nft_expr_priv(expr);
|
| 663 | unsigned int hooks;
|
| 664 |
|
| 665 | if (priv->key != NFT_META_PKTTYPE)
|
| 666 | return 0;
|
| 667 |
|
| 668 | switch (ctx->family) {
|
| 669 | case NFPROTO_BRIDGE:
|
| 670 | hooks = 1 << NF_BR_PRE_ROUTING;
|
| 671 | break;
|
| 672 | case NFPROTO_NETDEV:
|
| 673 | hooks = 1 << NF_NETDEV_INGRESS;
|
| 674 | break;
|
| 675 | case NFPROTO_IPV4:
|
| 676 | case NFPROTO_IPV6:
|
| 677 | case NFPROTO_INET:
|
| 678 | hooks = 1 << NF_INET_PRE_ROUTING;
|
| 679 | break;
|
| 680 | default:
|
| 681 | return -EOPNOTSUPP;
|
| 682 | }
|
| 683 |
|
| 684 | return nft_chain_validate_hooks(ctx->chain, hooks);
|
| 685 | }
|
| 686 | EXPORT_SYMBOL_GPL(nft_meta_set_validate);
|
| 687 |
|
| 688 | int nft_meta_set_init(const struct nft_ctx *ctx,
|
| 689 | const struct nft_expr *expr,
|
| 690 | const struct nlattr * const tb[])
|
| 691 | {
|
| 692 | struct nft_meta *priv = nft_expr_priv(expr);
|
| 693 | unsigned int len;
|
| 694 | int err;
|
| 695 |
|
| 696 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
|
| 697 | switch (priv->key) {
|
| 698 | case NFT_META_MARK:
|
| 699 | case NFT_META_PRIORITY:
|
| 700 | #ifdef CONFIG_NETWORK_SECMARK
|
| 701 | case NFT_META_SECMARK:
|
| 702 | #endif
|
| 703 | len = sizeof(u32);
|
| 704 | break;
|
| 705 | case NFT_META_NFTRACE:
|
| 706 | len = sizeof(u8);
|
| 707 | break;
|
| 708 | case NFT_META_PKTTYPE:
|
| 709 | len = sizeof(u8);
|
| 710 | break;
|
| 711 | default:
|
| 712 | return -EOPNOTSUPP;
|
| 713 | }
|
| 714 |
|
| 715 | priv->len = len;
|
| 716 | err = nft_parse_register_load(ctx, tb[NFTA_META_SREG], &priv->sreg, len);
|
| 717 | if (err < 0)
|
| 718 | return err;
|
| 719 |
|
| 720 | if (priv->key == NFT_META_NFTRACE)
|
| 721 | static_branch_inc(&nft_trace_enabled);
|
| 722 |
|
| 723 | return 0;
|
| 724 | }
|
| 725 | EXPORT_SYMBOL_GPL(nft_meta_set_init);
|
| 726 |
|
| 727 | int nft_meta_get_dump(struct sk_buff *skb,
|
| 728 | const struct nft_expr *expr, bool reset)
|
| 729 | {
|
| 730 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 731 |
|
| 732 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
|
| 733 | goto nla_put_failure;
|
| 734 | if (nft_dump_register(skb, NFTA_META_DREG, priv->dreg))
|
| 735 | goto nla_put_failure;
|
| 736 | return 0;
|
| 737 |
|
| 738 | nla_put_failure:
|
| 739 | return -1;
|
| 740 | }
|
| 741 | EXPORT_SYMBOL_GPL(nft_meta_get_dump);
|
| 742 |
|
| 743 | int nft_meta_set_dump(struct sk_buff *skb,
|
| 744 | const struct nft_expr *expr, bool reset)
|
| 745 | {
|
| 746 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 747 |
|
| 748 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key)))
|
| 749 | goto nla_put_failure;
|
| 750 | if (nft_dump_register(skb, NFTA_META_SREG, priv->sreg))
|
| 751 | goto nla_put_failure;
|
| 752 |
|
| 753 | return 0;
|
| 754 |
|
| 755 | nla_put_failure:
|
| 756 | return -1;
|
| 757 | }
|
| 758 | EXPORT_SYMBOL_GPL(nft_meta_set_dump);
|
| 759 |
|
| 760 | void nft_meta_set_destroy(const struct nft_ctx *ctx,
|
| 761 | const struct nft_expr *expr)
|
| 762 | {
|
| 763 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 764 |
|
| 765 | if (priv->key == NFT_META_NFTRACE)
|
| 766 | static_branch_dec(&nft_trace_enabled);
|
| 767 | }
|
| 768 | EXPORT_SYMBOL_GPL(nft_meta_set_destroy);
|
| 769 |
|
| 770 | static int nft_meta_get_offload(struct nft_offload_ctx *ctx,
|
| 771 | struct nft_flow_rule *flow,
|
| 772 | const struct nft_expr *expr)
|
| 773 | {
|
| 774 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 775 | struct nft_offload_reg *reg = &ctx->regs[priv->dreg];
|
| 776 |
|
| 777 | switch (priv->key) {
|
| 778 | case NFT_META_PROTOCOL:
|
| 779 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto,
|
| 780 | sizeof(__u16), reg);
|
| 781 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK);
|
| 782 | break;
|
| 783 | case NFT_META_L4PROTO:
|
| 784 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto,
|
| 785 | sizeof(__u8), reg);
|
| 786 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT);
|
| 787 | break;
|
| 788 | case NFT_META_IIF:
|
| 789 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta,
|
| 790 | ingress_ifindex, sizeof(__u32), reg);
|
| 791 | break;
|
| 792 | case NFT_META_IIFTYPE:
|
| 793 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta,
|
| 794 | ingress_iftype, sizeof(__u16), reg);
|
| 795 | break;
|
| 796 | default:
|
| 797 | return -EOPNOTSUPP;
|
| 798 | }
|
| 799 |
|
| 800 | return 0;
|
| 801 | }
|
| 802 |
|
| 803 | static const struct nft_expr_ops nft_meta_get_ops = {
|
| 804 | .type = &nft_meta_type,
|
| 805 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
|
| 806 | .eval = nft_meta_get_eval,
|
| 807 | .init = nft_meta_get_init,
|
| 808 | .dump = nft_meta_get_dump,
|
| 809 | .validate = nft_meta_get_validate,
|
| 810 | .offload = nft_meta_get_offload,
|
| 811 | };
|
| 812 |
|
| 813 | static const struct nft_expr_ops nft_meta_set_ops = {
|
| 814 | .type = &nft_meta_type,
|
| 815 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
|
| 816 | .eval = nft_meta_set_eval,
|
| 817 | .init = nft_meta_set_init,
|
| 818 | .destroy = nft_meta_set_destroy,
|
| 819 | .dump = nft_meta_set_dump,
|
| 820 | .validate = nft_meta_set_validate,
|
| 821 | };
|
| 822 |
|
| 823 | static const struct nft_expr_ops *
|
| 824 | nft_meta_select_ops(const struct nft_ctx *ctx,
|
| 825 | const struct nlattr * const tb[])
|
| 826 | {
|
| 827 | if (tb[NFTA_META_KEY] == NULL)
|
| 828 | return ERR_PTR(-EINVAL);
|
| 829 |
|
| 830 | if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
|
| 831 | return ERR_PTR(-EINVAL);
|
| 832 |
|
| 833 | #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) && IS_MODULE(CONFIG_NFT_BRIDGE_META)
|
| 834 | if (ctx->family == NFPROTO_BRIDGE)
|
| 835 | return ERR_PTR(-EAGAIN);
|
| 836 | #endif
|
| 837 | if (tb[NFTA_META_DREG])
|
| 838 | return &nft_meta_get_ops;
|
| 839 |
|
| 840 | if (tb[NFTA_META_SREG])
|
| 841 | return &nft_meta_set_ops;
|
| 842 |
|
| 843 | return ERR_PTR(-EINVAL);
|
| 844 | }
|
| 845 |
|
| 846 | static int nft_meta_inner_init(const struct nft_ctx *ctx,
|
| 847 | const struct nft_expr *expr,
|
| 848 | const struct nlattr * const tb[])
|
| 849 | {
|
| 850 | struct nft_meta *priv = nft_expr_priv(expr);
|
| 851 | unsigned int len;
|
| 852 |
|
| 853 | if (!tb[NFTA_META_KEY] || !tb[NFTA_META_DREG])
|
| 854 | return -EINVAL;
|
| 855 |
|
| 856 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
|
| 857 | switch (priv->key) {
|
| 858 | case NFT_META_PROTOCOL:
|
| 859 | len = sizeof(u16);
|
| 860 | break;
|
| 861 | case NFT_META_L4PROTO:
|
| 862 | len = sizeof(u32);
|
| 863 | break;
|
| 864 | default:
|
| 865 | return -EOPNOTSUPP;
|
| 866 | }
|
| 867 | priv->len = len;
|
| 868 |
|
| 869 | return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
|
| 870 | NULL, NFT_DATA_VALUE, len);
|
| 871 | }
|
| 872 |
|
| 873 | void nft_meta_inner_eval(const struct nft_expr *expr,
|
| 874 | struct nft_regs *regs,
|
| 875 | const struct nft_pktinfo *pkt,
|
| 876 | struct nft_inner_tun_ctx *tun_ctx)
|
| 877 | {
|
| 878 | const struct nft_meta *priv = nft_expr_priv(expr);
|
| 879 | u32 *dest = ®s->data[priv->dreg];
|
| 880 |
|
| 881 | switch (priv->key) {
|
| 882 | case NFT_META_PROTOCOL:
|
| 883 | nft_reg_store16(dest, (__force u16)tun_ctx->llproto);
|
| 884 | break;
|
| 885 | case NFT_META_L4PROTO:
|
| 886 | if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH))
|
| 887 | goto err;
|
| 888 |
|
| 889 | nft_reg_store8(dest, tun_ctx->l4proto);
|
| 890 | break;
|
| 891 | default:
|
| 892 | DEBUG_NET_WARN_ON_ONCE(1);
|
| 893 | goto err;
|
| 894 | }
|
| 895 | return;
|
| 896 |
|
| 897 | err:
|
| 898 | regs->verdict.code = NFT_BREAK;
|
| 899 | }
|
| 900 | EXPORT_SYMBOL_GPL(nft_meta_inner_eval);
|
| 901 |
|
| 902 | static const struct nft_expr_ops nft_meta_inner_ops = {
|
| 903 | .type = &nft_meta_type,
|
| 904 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
|
| 905 | .init = nft_meta_inner_init,
|
| 906 | .dump = nft_meta_get_dump,
|
| 907 |
|
| 908 | };
|
| 909 |
|
| 910 | struct nft_expr_type nft_meta_type __read_mostly = {
|
| 911 | .name = "meta",
|
| 912 | .select_ops = nft_meta_select_ops,
|
| 913 | .inner_ops = &nft_meta_inner_ops,
|
| 914 | .policy = nft_meta_policy,
|
| 915 | .maxattr = NFTA_META_MAX,
|
| 916 | .owner = THIS_MODULE,
|
| 917 | };
|
| 918 |
|
| 919 | #ifdef CONFIG_NETWORK_SECMARK
|
| 920 | struct nft_secmark {
|
| 921 | u32 secid;
|
| 922 | char *ctx;
|
| 923 | };
|
| 924 |
|
| 925 | static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = {
|
| 926 | [NFTA_SECMARK_CTX] = { .type = NLA_STRING, .len = NFT_SECMARK_CTX_MAXLEN },
|
| 927 | };
|
| 928 |
|
| 929 | static int nft_secmark_compute_secid(struct nft_secmark *priv)
|
| 930 | {
|
| 931 | u32 tmp_secid = 0;
|
| 932 | int err;
|
| 933 |
|
| 934 | err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid);
|
| 935 | if (err)
|
| 936 | return err;
|
| 937 |
|
| 938 | if (!tmp_secid)
|
| 939 | return -ENOENT;
|
| 940 |
|
| 941 | err = security_secmark_relabel_packet(tmp_secid);
|
| 942 | if (err)
|
| 943 | return err;
|
| 944 |
|
| 945 | priv->secid = tmp_secid;
|
| 946 | return 0;
|
| 947 | }
|
| 948 |
|
| 949 | static void nft_secmark_obj_eval(struct nft_object *obj, struct nft_regs *regs,
|
| 950 | const struct nft_pktinfo *pkt)
|
| 951 | {
|
| 952 | const struct nft_secmark *priv = nft_obj_data(obj);
|
| 953 | struct sk_buff *skb = pkt->skb;
|
| 954 |
|
| 955 | skb->secmark = priv->secid;
|
| 956 | }
|
| 957 |
|
| 958 | static int nft_secmark_obj_init(const struct nft_ctx *ctx,
|
| 959 | const struct nlattr * const tb[],
|
| 960 | struct nft_object *obj)
|
| 961 | {
|
| 962 | struct nft_secmark *priv = nft_obj_data(obj);
|
| 963 | int err;
|
| 964 |
|
| 965 | if (tb[NFTA_SECMARK_CTX] == NULL)
|
| 966 | return -EINVAL;
|
| 967 |
|
| 968 | priv->ctx = nla_strdup(tb[NFTA_SECMARK_CTX], GFP_KERNEL_ACCOUNT);
|
| 969 | if (!priv->ctx)
|
| 970 | return -ENOMEM;
|
| 971 |
|
| 972 | err = nft_secmark_compute_secid(priv);
|
| 973 | if (err) {
|
| 974 | kfree(priv->ctx);
|
| 975 | return err;
|
| 976 | }
|
| 977 |
|
| 978 | security_secmark_refcount_inc();
|
| 979 |
|
| 980 | return 0;
|
| 981 | }
|
| 982 |
|
| 983 | static int nft_secmark_obj_dump(struct sk_buff *skb, struct nft_object *obj,
|
| 984 | bool reset)
|
| 985 | {
|
| 986 | struct nft_secmark *priv = nft_obj_data(obj);
|
| 987 | int err;
|
| 988 |
|
| 989 | if (nla_put_string(skb, NFTA_SECMARK_CTX, priv->ctx))
|
| 990 | return -1;
|
| 991 |
|
| 992 | if (reset) {
|
| 993 | err = nft_secmark_compute_secid(priv);
|
| 994 | if (err)
|
| 995 | return err;
|
| 996 | }
|
| 997 |
|
| 998 | return 0;
|
| 999 | }
|
| 1000 |
|
| 1001 | static void nft_secmark_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
|
| 1002 | {
|
| 1003 | struct nft_secmark *priv = nft_obj_data(obj);
|
| 1004 |
|
| 1005 | security_secmark_refcount_dec();
|
| 1006 |
|
| 1007 | kfree(priv->ctx);
|
| 1008 | }
|
| 1009 |
|
| 1010 | static const struct nft_object_ops nft_secmark_obj_ops = {
|
| 1011 | .type = &nft_secmark_obj_type,
|
| 1012 | .size = sizeof(struct nft_secmark),
|
| 1013 | .init = nft_secmark_obj_init,
|
| 1014 | .eval = nft_secmark_obj_eval,
|
| 1015 | .dump = nft_secmark_obj_dump,
|
| 1016 | .destroy = nft_secmark_obj_destroy,
|
| 1017 | };
|
| 1018 | struct nft_object_type nft_secmark_obj_type __read_mostly = {
|
| 1019 | .type = NFT_OBJECT_SECMARK,
|
| 1020 | .ops = &nft_secmark_obj_ops,
|
| 1021 | .maxattr = NFTA_SECMARK_MAX,
|
| 1022 | .policy = nft_secmark_policy,
|
| 1023 | .owner = THIS_MODULE,
|
| 1024 | };
|
| 1025 | #endif
|