bpf: fix bpf_skb_in_cgroup helper naming
[deliverable/linux.git] / net / core / filter.c
index e759d90e8cef037ec117a7e70941c3be050e3103..bd9bf2e5fafafeaa6f270153bbfaae38a2031f33 100644 (file)
@@ -150,6 +150,12 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
        return raw_smp_processor_id();
 }
 
+static const struct bpf_func_proto bpf_get_raw_smp_processor_id_proto = {
+       .func           = __get_raw_cpu_id,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+};
+
 static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                              struct bpf_insn *insn_buf)
 {
@@ -748,6 +754,17 @@ static bool chk_code_allowed(u16 code_to_probe)
        return codes[code_to_probe];
 }
 
+static bool bpf_check_basics_ok(const struct sock_filter *filter,
+                               unsigned int flen)
+{
+       if (filter == NULL)
+               return false;
+       if (flen == 0 || flen > BPF_MAXINSNS)
+               return false;
+
+       return true;
+}
+
 /**
  *     bpf_check_classic - verify socket filter code
  *     @filter: filter to verify
@@ -768,9 +785,6 @@ static int bpf_check_classic(const struct sock_filter *filter,
        bool anc_found;
        int pc;
 
-       if (flen == 0 || flen > BPF_MAXINSNS)
-               return -EINVAL;
-
        /* Check the filter code now */
        for (pc = 0; pc < flen; pc++) {
                const struct sock_filter *ftest = &filter[pc];
@@ -1065,7 +1079,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
        struct bpf_prog *fp;
 
        /* Make sure new filter is there and in the right amounts. */
-       if (fprog->filter == NULL)
+       if (!bpf_check_basics_ok(fprog->filter, fprog->len))
                return -EINVAL;
 
        fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
@@ -1112,7 +1126,7 @@ int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
        int err;
 
        /* Make sure new filter is there and in the right amounts. */
-       if (fprog->filter == NULL)
+       if (!bpf_check_basics_ok(fprog->filter, fprog->len))
                return -EINVAL;
 
        fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
@@ -1207,7 +1221,6 @@ static
 struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
 {
        unsigned int fsize = bpf_classic_proglen(fprog);
-       unsigned int bpf_fsize = bpf_prog_size(fprog->len);
        struct bpf_prog *prog;
        int err;
 
@@ -1215,10 +1228,10 @@ struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
                return ERR_PTR(-EPERM);
 
        /* Make sure new filter is there and in the right amounts. */
-       if (fprog->filter == NULL)
+       if (!bpf_check_basics_ok(fprog->filter, fprog->len))
                return ERR_PTR(-EINVAL);
 
-       prog = bpf_prog_alloc(bpf_fsize, 0);
+       prog = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
        if (!prog)
                return ERR_PTR(-ENOMEM);
 
@@ -1288,21 +1301,10 @@ int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 
 static struct bpf_prog *__get_bpf(u32 ufd, struct sock *sk)
 {
-       struct bpf_prog *prog;
-
        if (sock_flag(sk, SOCK_FILTER_LOCKED))
                return ERR_PTR(-EPERM);
 
-       prog = bpf_prog_get(ufd);
-       if (IS_ERR(prog))
-               return prog;
-
-       if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) {
-               bpf_prog_put(prog);
-               return ERR_PTR(-EINVAL);
-       }
-
-       return prog;
+       return bpf_prog_get_type(ufd, BPF_PROG_TYPE_SOCKET_FILTER);
 }
 
 int sk_attach_bpf(u32 ufd, struct sock *sk)
@@ -1363,6 +1365,18 @@ static inline int bpf_try_make_writable(struct sk_buff *skb,
        return err;
 }
 
+static inline void bpf_push_mac_rcsum(struct sk_buff *skb)
+{
+       if (skb_at_tc_ingress(skb))
+               skb_postpush_rcsum(skb, skb_mac_header(skb), skb->mac_len);
+}
+
+static inline void bpf_pull_mac_rcsum(struct sk_buff *skb)
+{
+       if (skb_at_tc_ingress(skb))
+               skb_postpull_rcsum(skb, skb_mac_header(skb), skb->mac_len);
+}
+
 static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
 {
        struct bpf_scratchpad *sp = this_cpu_ptr(&bpf_sp);
@@ -1393,7 +1407,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
                return -EFAULT;
 
        if (flags & BPF_F_RECOMPUTE_CSUM)
-               skb_postpull_rcsum(skb, ptr, len);
+               __skb_postpull_rcsum(skb, ptr, len, offset);
 
        memcpy(ptr, from, len);
 
@@ -1402,7 +1416,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
                skb_store_bits(skb, offset, ptr, len);
 
        if (flags & BPF_F_RECOMPUTE_CSUM)
-               skb_postpush_rcsum(skb, ptr, len);
+               __skb_postpush_rcsum(skb, ptr, len, offset);
        if (flags & BPF_F_INVALIDATE_HASH)
                skb_clear_hash(skb);
 
@@ -1603,9 +1617,33 @@ static const struct bpf_func_proto bpf_csum_diff_proto = {
        .arg5_type      = ARG_ANYTHING,
 };
 
+static inline int __bpf_rx_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       return dev_forward_skb(dev, skb);
+}
+
+static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       int ret;
+
+       if (unlikely(__this_cpu_read(xmit_recursion) > XMIT_RECURSION_LIMIT)) {
+               net_crit_ratelimited("bpf: recursion limit reached on datapath, buggy bpf program?\n");
+               kfree_skb(skb);
+               return -ENETDOWN;
+       }
+
+       skb->dev = dev;
+
+       __this_cpu_inc(xmit_recursion);
+       ret = dev_queue_xmit(skb);
+       __this_cpu_dec(xmit_recursion);
+
+       return ret;
+}
+
 static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *) (long) r1, *skb2;
+       struct sk_buff *skb = (struct sk_buff *) (long) r1;
        struct net_device *dev;
 
        if (unlikely(flags & ~(BPF_F_INGRESS)))
@@ -1615,19 +1653,14 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
        if (unlikely(!dev))
                return -EINVAL;
 
-       skb2 = skb_clone(skb, GFP_ATOMIC);
-       if (unlikely(!skb2))
+       skb = skb_clone(skb, GFP_ATOMIC);
+       if (unlikely(!skb))
                return -ENOMEM;
 
-       if (flags & BPF_F_INGRESS) {
-               if (skb_at_tc_ingress(skb2))
-                       skb_postpush_rcsum(skb2, skb_mac_header(skb2),
-                                          skb2->mac_len);
-               return dev_forward_skb(dev, skb2);
-       }
+       bpf_push_mac_rcsum(skb);
 
-       skb2->dev = dev;
-       return dev_queue_xmit(skb2);
+       return flags & BPF_F_INGRESS ?
+              __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);
 }
 
 static const struct bpf_func_proto bpf_clone_redirect_proto = {
@@ -1671,15 +1704,10 @@ int skb_do_redirect(struct sk_buff *skb)
                return -EINVAL;
        }
 
-       if (ri->flags & BPF_F_INGRESS) {
-               if (skb_at_tc_ingress(skb))
-                       skb_postpush_rcsum(skb, skb_mac_header(skb),
-                                          skb->mac_len);
-               return dev_forward_skb(dev, skb);
-       }
+       bpf_push_mac_rcsum(skb);
 
-       skb->dev = dev;
-       return dev_queue_xmit(skb);
+       return ri->flags & BPF_F_INGRESS ?
+              __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);
 }
 
 static const struct bpf_func_proto bpf_redirect_proto = {
@@ -1714,6 +1742,23 @@ static const struct bpf_func_proto bpf_get_route_realm_proto = {
        .arg1_type      = ARG_PTR_TO_CTX,
 };
 
+static u64 bpf_get_hash_recalc(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       /* If skb_clear_hash() was called due to mangling, we can
+        * trigger SW recalculation here. Later access to hash
+        * can then use the inline skb->hash via context directly
+        * instead of calling this helper again.
+        */
+       return skb_get_hash((struct sk_buff *) (unsigned long) r1);
+}
+
+static const struct bpf_func_proto bpf_get_hash_recalc_proto = {
+       .func           = bpf_get_hash_recalc,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+};
+
 static u64 bpf_skb_vlan_push(u64 r1, u64 r2, u64 vlan_tci, u64 r4, u64 r5)
 {
        struct sk_buff *skb = (struct sk_buff *) (long) r1;
@@ -1724,7 +1769,10 @@ static u64 bpf_skb_vlan_push(u64 r1, u64 r2, u64 vlan_tci, u64 r4, u64 r5)
                     vlan_proto != htons(ETH_P_8021AD)))
                vlan_proto = htons(ETH_P_8021Q);
 
+       bpf_push_mac_rcsum(skb);
        ret = skb_vlan_push(skb, vlan_proto, vlan_tci);
+       bpf_pull_mac_rcsum(skb);
+
        bpf_compute_data_end(skb);
        return ret;
 }
@@ -1744,7 +1792,10 @@ static u64 bpf_skb_vlan_pop(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
        struct sk_buff *skb = (struct sk_buff *) (long) r1;
        int ret;
 
+       bpf_push_mac_rcsum(skb);
        ret = skb_vlan_pop(skb);
+       bpf_pull_mac_rcsum(skb);
+
        bpf_compute_data_end(skb);
        return ret;
 }
@@ -1757,6 +1808,224 @@ const struct bpf_func_proto bpf_skb_vlan_pop_proto = {
 };
 EXPORT_SYMBOL_GPL(bpf_skb_vlan_pop_proto);
 
+static int bpf_skb_generic_push(struct sk_buff *skb, u32 off, u32 len)
+{
+       /* Caller already did skb_cow() with len as headroom,
+        * so no need to do it here.
+        */
+       skb_push(skb, len);
+       memmove(skb->data, skb->data + len, off);
+       memset(skb->data + off, 0, len);
+
+       /* No skb_postpush_rcsum(skb, skb->data + off, len)
+        * needed here as it does not change the skb->csum
+        * result for checksum complete when summing over
+        * zeroed blocks.
+        */
+       return 0;
+}
+
+static int bpf_skb_generic_pop(struct sk_buff *skb, u32 off, u32 len)
+{
+       /* skb_ensure_writable() is not needed here, as we're
+        * already working on an uncloned skb.
+        */
+       if (unlikely(!pskb_may_pull(skb, off + len)))
+               return -ENOMEM;
+
+       skb_postpull_rcsum(skb, skb->data + off, len);
+       memmove(skb->data + len, skb->data, off);
+       __skb_pull(skb, len);
+
+       return 0;
+}
+
+static int bpf_skb_net_hdr_push(struct sk_buff *skb, u32 off, u32 len)
+{
+       bool trans_same = skb->transport_header == skb->network_header;
+       int ret;
+
+       /* There's no need for __skb_push()/__skb_pull() pair to
+        * get to the start of the mac header as we're guaranteed
+        * to always start from here under eBPF.
+        */
+       ret = bpf_skb_generic_push(skb, off, len);
+       if (likely(!ret)) {
+               skb->mac_header -= len;
+               skb->network_header -= len;
+               if (trans_same)
+                       skb->transport_header = skb->network_header;
+       }
+
+       return ret;
+}
+
+static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
+{
+       bool trans_same = skb->transport_header == skb->network_header;
+       int ret;
+
+       /* Same here, __skb_push()/__skb_pull() pair not needed. */
+       ret = bpf_skb_generic_pop(skb, off, len);
+       if (likely(!ret)) {
+               skb->mac_header += len;
+               skb->network_header += len;
+               if (trans_same)
+                       skb->transport_header = skb->network_header;
+       }
+
+       return ret;
+}
+
+static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
+{
+       const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+       u32 off = skb->network_header - skb->mac_header;
+       int ret;
+
+       ret = skb_cow(skb, len_diff);
+       if (unlikely(ret < 0))
+               return ret;
+
+       ret = bpf_skb_net_hdr_push(skb, off, len_diff);
+       if (unlikely(ret < 0))
+               return ret;
+
+       if (skb_is_gso(skb)) {
+               /* SKB_GSO_UDP stays as is. SKB_GSO_TCPV4 needs to
+                * be changed into SKB_GSO_TCPV6.
+                */
+               if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
+                       skb_shinfo(skb)->gso_type &= ~SKB_GSO_TCPV4;
+                       skb_shinfo(skb)->gso_type |=  SKB_GSO_TCPV6;
+               }
+
+               /* Due to IPv6 header, MSS needs to be downgraded. */
+               skb_shinfo(skb)->gso_size -= len_diff;
+               /* Header must be checked, and gso_segs recomputed. */
+               skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+               skb_shinfo(skb)->gso_segs = 0;
+       }
+
+       skb->protocol = htons(ETH_P_IPV6);
+       skb_clear_hash(skb);
+
+       return 0;
+}
+
+static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
+{
+       const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+       u32 off = skb->network_header - skb->mac_header;
+       int ret;
+
+       ret = skb_unclone(skb, GFP_ATOMIC);
+       if (unlikely(ret < 0))
+               return ret;
+
+       ret = bpf_skb_net_hdr_pop(skb, off, len_diff);
+       if (unlikely(ret < 0))
+               return ret;
+
+       if (skb_is_gso(skb)) {
+               /* SKB_GSO_UDP stays as is. SKB_GSO_TCPV6 needs to
+                * be changed into SKB_GSO_TCPV4.
+                */
+               if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) {
+                       skb_shinfo(skb)->gso_type &= ~SKB_GSO_TCPV6;
+                       skb_shinfo(skb)->gso_type |=  SKB_GSO_TCPV4;
+               }
+
+               /* Due to IPv4 header, MSS can be upgraded. */
+               skb_shinfo(skb)->gso_size += len_diff;
+               /* Header must be checked, and gso_segs recomputed. */
+               skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+               skb_shinfo(skb)->gso_segs = 0;
+       }
+
+       skb->protocol = htons(ETH_P_IP);
+       skb_clear_hash(skb);
+
+       return 0;
+}
+
+static int bpf_skb_proto_xlat(struct sk_buff *skb, __be16 to_proto)
+{
+       __be16 from_proto = skb->protocol;
+
+       if (from_proto == htons(ETH_P_IP) &&
+             to_proto == htons(ETH_P_IPV6))
+               return bpf_skb_proto_4_to_6(skb);
+
+       if (from_proto == htons(ETH_P_IPV6) &&
+             to_proto == htons(ETH_P_IP))
+               return bpf_skb_proto_6_to_4(skb);
+
+       return -ENOTSUPP;
+}
+
+static u64 bpf_skb_change_proto(u64 r1, u64 r2, u64 flags, u64 r4, u64 r5)
+{
+       struct sk_buff *skb = (struct sk_buff *) (long) r1;
+       __be16 proto = (__force __be16) r2;
+       int ret;
+
+       if (unlikely(flags))
+               return -EINVAL;
+
+       /* General idea is that this helper does the basic groundwork
+        * needed for changing the protocol, and eBPF program fills the
+        * rest through bpf_skb_store_bytes(), bpf_lX_csum_replace()
+        * and other helpers, rather than passing a raw buffer here.
+        *
+        * The rationale is to keep this minimal and without a need to
+        * deal with raw packet data. F.e. even if we would pass buffers
+        * here, the program still needs to call the bpf_lX_csum_replace()
+        * helpers anyway. Plus, this way we keep also separation of
+        * concerns, since f.e. bpf_skb_store_bytes() should only take
+        * care of stores.
+        *
+        * Currently, additional options and extension header space are
+        * not supported, but flags register is reserved so we can adapt
+        * that. For offloads, we mark packet as dodgy, so that headers
+        * need to be verified first.
+        */
+       ret = bpf_skb_proto_xlat(skb, proto);
+       bpf_compute_data_end(skb);
+       return ret;
+}
+
+static const struct bpf_func_proto bpf_skb_change_proto_proto = {
+       .func           = bpf_skb_change_proto,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+};
+
+static u64 bpf_skb_change_type(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       struct sk_buff *skb = (struct sk_buff *) (long) r1;
+       u32 pkt_type = r2;
+
+       /* We only allow a restricted subset to be changed for now. */
+       if (unlikely(skb->pkt_type > PACKET_OTHERHOST ||
+                    pkt_type > PACKET_OTHERHOST))
+               return -EINVAL;
+
+       skb->pkt_type = pkt_type;
+       return 0;
+}
+
+static const struct bpf_func_proto bpf_skb_change_type_proto = {
+       .func           = bpf_skb_change_type,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+};
+
 bool bpf_helper_changes_skb_data(void *func)
 {
        if (func == bpf_skb_vlan_push)
@@ -1765,6 +2034,8 @@ bool bpf_helper_changes_skb_data(void *func)
                return true;
        if (func == bpf_skb_store_bytes)
                return true;
+       if (func == bpf_skb_change_proto)
+               return true;
        if (func == bpf_l3_csum_replace)
                return true;
        if (func == bpf_l4_csum_replace)
@@ -1773,6 +2044,47 @@ bool bpf_helper_changes_skb_data(void *func)
        return false;
 }
 
+static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,
+                                 unsigned long off, unsigned long len)
+{
+       void *ptr = skb_header_pointer(skb, off, len, dst_buff);
+
+       if (unlikely(!ptr))
+               return len;
+       if (ptr != dst_buff)
+               memcpy(dst_buff, ptr, len);
+
+       return 0;
+}
+
+static u64 bpf_skb_event_output(u64 r1, u64 r2, u64 flags, u64 r4,
+                               u64 meta_size)
+{
+       struct sk_buff *skb = (struct sk_buff *)(long) r1;
+       struct bpf_map *map = (struct bpf_map *)(long) r2;
+       u64 skb_size = (flags & BPF_F_CTXLEN_MASK) >> 32;
+       void *meta = (void *)(long) r4;
+
+       if (unlikely(flags & ~(BPF_F_CTXLEN_MASK | BPF_F_INDEX_MASK)))
+               return -EINVAL;
+       if (unlikely(skb_size > skb->len))
+               return -EFAULT;
+
+       return bpf_event_output(map, flags, meta, meta_size, skb, skb_size,
+                               bpf_skb_copy);
+}
+
+static const struct bpf_func_proto bpf_skb_event_output_proto = {
+       .func           = bpf_skb_event_output,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_STACK,
+       .arg5_type      = ARG_CONST_STACK_SIZE,
+};
+
 static unsigned short bpf_tunnel_key_af(u64 flags)
 {
        return flags & BPF_F_TUNINFO_IPV6 ? AF_INET6 : AF_INET;
@@ -2004,6 +2316,40 @@ bpf_get_skb_set_tunnel_proto(enum bpf_func_id which)
        }
 }
 
+#ifdef CONFIG_SOCK_CGROUP_DATA
+static u64 bpf_skb_under_cgroup(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+       struct sk_buff *skb = (struct sk_buff *)(long)r1;
+       struct bpf_map *map = (struct bpf_map *)(long)r2;
+       struct bpf_array *array = container_of(map, struct bpf_array, map);
+       struct cgroup *cgrp;
+       struct sock *sk;
+       u32 i = (u32)r3;
+
+       sk = skb->sk;
+       if (!sk || !sk_fullsock(sk))
+               return -ENOENT;
+
+       if (unlikely(i >= array->map.max_entries))
+               return -E2BIG;
+
+       cgrp = READ_ONCE(array->ptrs[i]);
+       if (unlikely(!cgrp))
+               return -EAGAIN;
+
+       return cgroup_is_descendant(sock_cgroup_ptr(&sk->sk_cgrp_data), cgrp);
+}
+
+static const struct bpf_func_proto bpf_skb_under_cgroup_proto = {
+       .func           = bpf_skb_under_cgroup,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+};
+#endif
+
 static const struct bpf_func_proto *
 sk_filter_func_proto(enum bpf_func_id func_id)
 {
@@ -2017,7 +2363,7 @@ sk_filter_func_proto(enum bpf_func_id func_id)
        case BPF_FUNC_get_prandom_u32:
                return &bpf_get_prandom_u32_proto;
        case BPF_FUNC_get_smp_processor_id:
-               return &bpf_get_smp_processor_id_proto;
+               return &bpf_get_raw_smp_processor_id_proto;
        case BPF_FUNC_tail_call:
                return &bpf_tail_call_proto;
        case BPF_FUNC_ktime_get_ns:
@@ -2052,6 +2398,10 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
                return &bpf_skb_vlan_push_proto;
        case BPF_FUNC_skb_vlan_pop:
                return &bpf_skb_vlan_pop_proto;
+       case BPF_FUNC_skb_change_proto:
+               return &bpf_skb_change_proto_proto;
+       case BPF_FUNC_skb_change_type:
+               return &bpf_skb_change_type_proto;
        case BPF_FUNC_skb_get_tunnel_key:
                return &bpf_skb_get_tunnel_key_proto;
        case BPF_FUNC_skb_set_tunnel_key:
@@ -2064,13 +2414,27 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
                return &bpf_redirect_proto;
        case BPF_FUNC_get_route_realm:
                return &bpf_get_route_realm_proto;
+       case BPF_FUNC_get_hash_recalc:
+               return &bpf_get_hash_recalc_proto;
        case BPF_FUNC_perf_event_output:
-               return bpf_get_event_output_proto();
+               return &bpf_skb_event_output_proto;
+       case BPF_FUNC_get_smp_processor_id:
+               return &bpf_get_smp_processor_id_proto;
+#ifdef CONFIG_SOCK_CGROUP_DATA
+       case BPF_FUNC_skb_under_cgroup:
+               return &bpf_skb_under_cgroup_proto;
+#endif
        default:
                return sk_filter_func_proto(func_id);
        }
 }
 
+static const struct bpf_func_proto *
+xdp_func_proto(enum bpf_func_id func_id)
+{
+       return sk_filter_func_proto(func_id);
+}
+
 static bool __is_valid_access(int off, int size, enum bpf_access_type type)
 {
        if (off < 0 || off >= sizeof(struct __sk_buff))
@@ -2138,6 +2502,44 @@ static bool tc_cls_act_is_valid_access(int off, int size,
        return __is_valid_access(off, size, type);
 }
 
+static bool __is_valid_xdp_access(int off, int size,
+                                 enum bpf_access_type type)
+{
+       if (off < 0 || off >= sizeof(struct xdp_md))
+               return false;
+       if (off % size != 0)
+               return false;
+       if (size != 4)
+               return false;
+
+       return true;
+}
+
+static bool xdp_is_valid_access(int off, int size,
+                               enum bpf_access_type type,
+                               enum bpf_reg_type *reg_type)
+{
+       if (type == BPF_WRITE)
+               return false;
+
+       switch (off) {
+       case offsetof(struct xdp_md, data):
+               *reg_type = PTR_TO_PACKET;
+               break;
+       case offsetof(struct xdp_md, data_end):
+               *reg_type = PTR_TO_PACKET_END;
+               break;
+       }
+
+       return __is_valid_xdp_access(off, size, type);
+}
+
+void bpf_warn_invalid_xdp_action(u32 act)
+{
+       WARN_ONCE(1, "Illegal XDP return value %u, expect packet loss\n", act);
+}
+EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action);
+
 static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
                                      int src_reg, int ctx_off,
                                      struct bpf_insn *insn_buf,
@@ -2289,6 +2691,29 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
        return insn - insn_buf;
 }
 
+static u32 xdp_convert_ctx_access(enum bpf_access_type type, int dst_reg,
+                                 int src_reg, int ctx_off,
+                                 struct bpf_insn *insn_buf,
+                                 struct bpf_prog *prog)
+{
+       struct bpf_insn *insn = insn_buf;
+
+       switch (ctx_off) {
+       case offsetof(struct xdp_md, data):
+               *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct xdp_buff, data)),
+                                     dst_reg, src_reg,
+                                     offsetof(struct xdp_buff, data));
+               break;
+       case offsetof(struct xdp_md, data_end):
+               *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct xdp_buff, data_end)),
+                                     dst_reg, src_reg,
+                                     offsetof(struct xdp_buff, data_end));
+               break;
+       }
+
+       return insn - insn_buf;
+}
+
 static const struct bpf_verifier_ops sk_filter_ops = {
        .get_func_proto         = sk_filter_func_proto,
        .is_valid_access        = sk_filter_is_valid_access,
@@ -2301,6 +2726,12 @@ static const struct bpf_verifier_ops tc_cls_act_ops = {
        .convert_ctx_access     = bpf_net_convert_ctx_access,
 };
 
+static const struct bpf_verifier_ops xdp_ops = {
+       .get_func_proto         = xdp_func_proto,
+       .is_valid_access        = xdp_is_valid_access,
+       .convert_ctx_access     = xdp_convert_ctx_access,
+};
+
 static struct bpf_prog_type_list sk_filter_type __read_mostly = {
        .ops    = &sk_filter_ops,
        .type   = BPF_PROG_TYPE_SOCKET_FILTER,
@@ -2316,11 +2747,17 @@ static struct bpf_prog_type_list sched_act_type __read_mostly = {
        .type   = BPF_PROG_TYPE_SCHED_ACT,
 };
 
+static struct bpf_prog_type_list xdp_type __read_mostly = {
+       .ops    = &xdp_ops,
+       .type   = BPF_PROG_TYPE_XDP,
+};
+
 static int __init register_sk_filter_ops(void)
 {
        bpf_register_prog_type(&sk_filter_type);
        bpf_register_prog_type(&sched_cls_type);
        bpf_register_prog_type(&sched_act_type);
+       bpf_register_prog_type(&xdp_type);
 
        return 0;
 }
This page took 0.131407 seconds and 5 git commands to generate.