History

bpf: Add IPv6 Segment Routing helpers

The BPF seg6local hook should be powerful enough to enable users to implement most of the use-cases one could think of. After some thinking, we figured out that the following actions should be possible on a SRv6 packet, requiring 3 specific helpers : - bpf_lwt_seg6_store_bytes: Modify non-sensitive fields of the SRH - bpf_lwt_seg6_adjust_srh: Allow to grow or shrink a SRH (to add/delete TLVs) - bpf_lwt_seg6_action: Apply some SRv6 network programming actions (specifically End.X, End.T, End.B6 and End.B6.Encap) The specifications of these helpers are provided in the patch (see include/uapi/linux/bpf.h). The non-sensitive fields of the SRH are the following : flags, tag and TLVs. The other fields can not be modified, to maintain the SRH integrity. Flags, tag and TLVs can easily be modified as their validity can be checked afterwards via seg6_validate_srh. It is not allowed to modify the segments directly. If one wants to add segments on the path, he should stack a new SRH using the End.B6 action via bpf_lwt_seg6_action. Growing, shrinking or editing TLVs via the helpers will flag the SRH as invalid, and it will have to be re-validated before re-entering the IPv6 layer. This flag is stored in a per-CPU buffer, along with the current header length in bytes. Storing the SRH len in bytes in the control block is mandatory when using bpf_lwt_seg6_adjust_srh. The Header Ext. Length field contains the SRH len rounded to 8 bytes (a padding TLV can be inserted to ensure the 8-bytes boundary). When adding/deleting TLVs within the BPF program, the SRH may temporary be in an invalid state where its length cannot be rounded to 8 bytes without remainder, hence the need to store the length in bytes separately. The caller of the BPF program can then ensure that the SRH's final length is valid using this value. Again, a final SRH modified by a BPF program which doesn’t respect the 8-bytes boundary will be discarded as it will be considered as invalid. Finally, a fourth helper is provided, bpf_lwt_push_encap, which is available from the LWT BPF IN hook, but not from the seg6local BPF one. This helper allows to encapsulate a Segment Routing Header (either with a new outer IPv6 header, or by inlining it directly in the existing IPv6 header) into a non-SRv6 packet. This helper is required if we want to offer the possibility to dynamically encapsulate a SRH for non-SRv6 packet, as the BPF seg6local hook only works on traffic already containing a SRH. This is the BPF equivalent of the seg6 LWT infrastructure, which achieves the same purpose but with a static SRH per route. These helpers require CONFIG_IPV6=y (and not =m).

Mathieu Xhonneux committed 8 years ago

Commit fe94cc21 parent 1c1e7615 files · +372 −24Browse files

Unified · Split

5 files
  1. include/net/seg6_local.h +8 −0
  2. include/uapi/linux/bpf.h +95 −1
  3. net/core/filter.c +262 −23
  4. net/ipv6/Kconfig +5 −0
  5. net/ipv6/seg6_local.c +2 −0
include/net/seg6_local.h+8−0
@@ -15,10 +15,18 @@ · 14 unmodified lines
15#ifndef _NET_SEG6_LOCAL_H15#ifndef _NET_SEG6_LOCAL_H
16#define _NET_SEG6_LOCAL_H16#define _NET_SEG6_LOCAL_H
17 17
18#include <linux/percpu.h>
18#include <linux/net.h>19#include <linux/net.h>
19#include <linux/ipv6.h>20#include <linux/ipv6.h>
20 21
21extern int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,22extern int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
22 u32 tbl_id);23 u32 tbl_id);
23 24
25struct seg6_bpf_srh_state {
26 bool valid;
27 u16 hdrlen;
28};
29
30DECLARE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
31
24#endif32#endif
include/uapi/linux/bpf.h+95−1
@@ -1902,6 +1902,90 @@ union bpf_attr { · 1901 unmodified lines
1902 * egress otherwise). This is the only flag supported for now.1902 * egress otherwise). This is the only flag supported for now.
1903 * Return1903 * Return
1904 * **SK_PASS** on success, or **SK_DROP** on error.1904 * **SK_PASS** on success, or **SK_DROP** on error.
1905 *
1906 * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
1907 * Description
1908 * Encapsulate the packet associated to *skb* within a Layer 3
1909 * protocol header. This header is provided in the buffer at
1910 * address *hdr*, with *len* its size in bytes. *type* indicates
1911 * the protocol of the header and can be one of:
1912 *
1913 * **BPF_LWT_ENCAP_SEG6**
1914 * IPv6 encapsulation with Segment Routing Header
1915 * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
1916 * the IPv6 header is computed by the kernel.
1917 * **BPF_LWT_ENCAP_SEG6_INLINE**
1918 * Only works if *skb* contains an IPv6 packet. Insert a
1919 * Segment Routing Header (**struct ipv6_sr_hdr**) inside
1920 * the IPv6 header.
1921 *
1922 * A call to this helper is susceptible to change the underlaying
1923 * packet buffer. Therefore, at load time, all checks on pointers
1924 * previously done by the verifier are invalidated and must be
1925 * performed again, if the helper is used in combination with
1926 * direct packet access.
1927 * Return
1928 * 0 on success, or a negative error in case of failure.
1929 *
1930 * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len)
1931 * Description
1932 * Store *len* bytes from address *from* into the packet
1933 * associated to *skb*, at *offset*. Only the flags, tag and TLVs
1934 * inside the outermost IPv6 Segment Routing Header can be
1935 * modified through this helper.
1936 *
1937 * A call to this helper is susceptible to change the underlaying
1938 * packet buffer. Therefore, at load time, all checks on pointers
1939 * previously done by the verifier are invalidated and must be
1940 * performed again, if the helper is used in combination with
1941 * direct packet access.
1942 * Return
1943 * 0 on success, or a negative error in case of failure.
1944 *
1945 * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta)
1946 * Description
1947 * Adjust the size allocated to TLVs in the outermost IPv6
1948 * Segment Routing Header contained in the packet associated to
1949 * *skb*, at position *offset* by *delta* bytes. Only offsets
1950 * after the segments are accepted. *delta* can be as well
1951 * positive (growing) as negative (shrinking).
1952 *
1953 * A call to this helper is susceptible to change the underlaying
1954 * packet buffer. Therefore, at load time, all checks on pointers
1955 * previously done by the verifier are invalidated and must be
1956 * performed again, if the helper is used in combination with
1957 * direct packet access.
1958 * Return
1959 * 0 on success, or a negative error in case of failure.
1960 *
1961 * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len)
1962 * Description
1963 * Apply an IPv6 Segment Routing action of type *action* to the
1964 * packet associated to *skb*. Each action takes a parameter
1965 * contained at address *param*, and of length *param_len* bytes.
1966 * *action* can be one of:
1967 *
1968 * **SEG6_LOCAL_ACTION_END_X**
1969 * End.X action: Endpoint with Layer-3 cross-connect.
1970 * Type of *param*: **struct in6_addr**.
1971 * **SEG6_LOCAL_ACTION_END_T**
1972 * End.T action: Endpoint with specific IPv6 table lookup.
1973 * Type of *param*: **int**.
1974 * **SEG6_LOCAL_ACTION_END_B6**
1975 * End.B6 action: Endpoint bound to an SRv6 policy.
1976 * Type of param: **struct ipv6_sr_hdr**.
1977 * **SEG6_LOCAL_ACTION_END_B6_ENCAP**
1978 * End.B6.Encap action: Endpoint bound to an SRv6
1979 * encapsulation policy.
1980 * Type of param: **struct ipv6_sr_hdr**.
1981 *
1982 * A call to this helper is susceptible to change the underlaying
1983 * packet buffer. Therefore, at load time, all checks on pointers
1984 * previously done by the verifier are invalidated and must be
1985 * performed again, if the helper is used in combination with
1986 * direct packet access.
1987 * Return
1988 * 0 on success, or a negative error in case of failure.
1905 */1989 */
1906#define __BPF_FUNC_MAPPER(FN) \1990#define __BPF_FUNC_MAPPER(FN) \
1907 FN(unspec), \1991 FN(unspec), \
@@ -1976,7 +2060,11 @@ union bpf_attr { · 68 unmodified lines
1976 FN(fib_lookup), \2060 FN(fib_lookup), \
1977 FN(sock_hash_update), \2061 FN(sock_hash_update), \
1978 FN(msg_redirect_hash), \2062 FN(msg_redirect_hash), \
1979 FN(sk_redirect_hash),2063 FN(sk_redirect_hash), \
2064 FN(lwt_push_encap), \
2065 FN(lwt_seg6_store_bytes), \
2066 FN(lwt_seg6_adjust_srh), \
2067 FN(lwt_seg6_action),
1980 2068
1981/* integer value in 'imm' field of BPF_CALL instruction selects which helper2069/* integer value in 'imm' field of BPF_CALL instruction selects which helper
1982 * function eBPF program intends to call2070 * function eBPF program intends to call
@@ -2043,6 +2131,12 @@ enum bpf_hdr_start_off { · 60 unmodified lines
2043 BPF_HDR_START_NET,2131 BPF_HDR_START_NET,
2044};2132};
2045 2133
2134/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
2135enum bpf_lwt_encap_mode {
2136 BPF_LWT_ENCAP_SEG6,
2137 BPF_LWT_ENCAP_SEG6_INLINE
2138};
2139
2046/* user accessible mirror of in-kernel sk_buff.2140/* user accessible mirror of in-kernel sk_buff.
2047 * new fields can only be added to the end of this structure2141 * new fields can only be added to the end of this structure
2048 */2142 */
net/core/filter.c+262−23
@@ -64,6 +64,10 @@ · 63 unmodified lines
64#include <net/ip_fib.h>64#include <net/ip_fib.h>
65#include <net/flow.h>65#include <net/flow.h>
66#include <net/arp.h>66#include <net/arp.h>
67#include <net/ipv6.h>
68#include <linux/seg6_local.h>
69#include <net/seg6.h>
70#include <net/seg6_local.h>
67 71
68/**72/**
69 * sk_filter_trim_cap - run a packet through a socket filter73 * sk_filter_trim_cap - run a packet through a socket filter
@@ -3363,28 +3367,6 @@ static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { · 3293 unmodified lines
3363 .arg3_type = ARG_ANYTHING,3367 .arg3_type = ARG_ANYTHING,
3364};3368};
3365 3369
3366bool bpf_helper_changes_pkt_data(void *func)
3367{
3368 if (func == bpf_skb_vlan_push ||
3369 func == bpf_skb_vlan_pop ||
3370 func == bpf_skb_store_bytes ||
3371 func == bpf_skb_change_proto ||
3372 func == bpf_skb_change_head ||
3373 func == bpf_skb_change_tail ||
3374 func == bpf_skb_adjust_room ||
3375 func == bpf_skb_pull_data ||
3376 func == bpf_clone_redirect ||
3377 func == bpf_l3_csum_replace ||
3378 func == bpf_l4_csum_replace ||
3379 func == bpf_xdp_adjust_head ||
3380 func == bpf_xdp_adjust_meta ||
3381 func == bpf_msg_pull_data ||
3382 func == bpf_xdp_adjust_tail)
3383 return true;
3384
3385 return false;
3386}
3387
3388static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,3370static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,
3389 unsigned long off, unsigned long len)3371 unsigned long off, unsigned long len)
3390{3372{
@@ -4360,6 +4342,264 @@ static const struct bpf_func_proto bpf_skb_fib_lookup_proto = { · 969 unmodified lines
4360 .arg4_type = ARG_ANYTHING,4342 .arg4_type = ARG_ANYTHING,
4361};4343};
4362 4344
4345#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4346static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
4347{
4348 int err;
4349 struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)hdr;
4350
4351 if (!seg6_validate_srh(srh, len))
4352 return -EINVAL;
4353
4354 switch (type) {
4355 case BPF_LWT_ENCAP_SEG6_INLINE:
4356 if (skb->protocol != htons(ETH_P_IPV6))
4357 return -EBADMSG;
4358
4359 err = seg6_do_srh_inline(skb, srh);
4360 break;
4361 case BPF_LWT_ENCAP_SEG6:
4362 skb_reset_inner_headers(skb);
4363 skb->encapsulation = 1;
4364 err = seg6_do_srh_encap(skb, srh, IPPROTO_IPV6);
4365 break;
4366 default:
4367 return -EINVAL;
4368 }
4369
4370 bpf_compute_data_pointers(skb);
4371 if (err)
4372 return err;
4373
4374 ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
4375 skb_set_transport_header(skb, sizeof(struct ipv6hdr));
4376
4377 return seg6_lookup_nexthop(skb, NULL, 0);
4378}
4379#endif /* CONFIG_IPV6_SEG6_BPF */
4380
4381BPF_CALL_4(bpf_lwt_push_encap, struct sk_buff *, skb, u32, type, void *, hdr,
4382 u32, len)
4383{
4384 switch (type) {
4385#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4386 case BPF_LWT_ENCAP_SEG6:
4387 case BPF_LWT_ENCAP_SEG6_INLINE:
4388 return bpf_push_seg6_encap(skb, type, hdr, len);
4389#endif
4390 default:
4391 return -EINVAL;
4392 }
4393}
4394
4395static const struct bpf_func_proto bpf_lwt_push_encap_proto = {
4396 .func = bpf_lwt_push_encap,
4397 .gpl_only = false,
4398 .ret_type = RET_INTEGER,
4399 .arg1_type = ARG_PTR_TO_CTX,
4400 .arg2_type = ARG_ANYTHING,
4401 .arg3_type = ARG_PTR_TO_MEM,
4402 .arg4_type = ARG_CONST_SIZE
4403};
4404
4405BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset,
4406 const void *, from, u32, len)
4407{
4408#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4409 struct seg6_bpf_srh_state *srh_state =
4410 this_cpu_ptr(&seg6_bpf_srh_states);
4411 void *srh_tlvs, *srh_end, *ptr;
4412 struct ipv6_sr_hdr *srh;
4413 int srhoff = 0;
4414
4415 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4416 return -EINVAL;
4417
4418 srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4419 srh_tlvs = (void *)((char *)srh + ((srh->first_segment + 1) << 4));
4420 srh_end = (void *)((char *)srh + sizeof(*srh) + srh_state->hdrlen);
4421
4422 ptr = skb->data + offset;
4423 if (ptr >= srh_tlvs && ptr + len <= srh_end)
4424 srh_state->valid = 0;
4425 else if (ptr < (void *)&srh->flags ||
4426 ptr + len > (void *)&srh->segments)
4427 return -EFAULT;
4428
4429 if (unlikely(bpf_try_make_writable(skb, offset + len)))
4430 return -EFAULT;
4431
4432 memcpy(skb->data + offset, from, len);
4433 return 0;
4434#else /* CONFIG_IPV6_SEG6_BPF */
4435 return -EOPNOTSUPP;
4436#endif
4437}
4438
4439static const struct bpf_func_proto bpf_lwt_seg6_store_bytes_proto = {
4440 .func = bpf_lwt_seg6_store_bytes,
4441 .gpl_only = false,
4442 .ret_type = RET_INTEGER,
4443 .arg1_type = ARG_PTR_TO_CTX,
4444 .arg2_type = ARG_ANYTHING,
4445 .arg3_type = ARG_PTR_TO_MEM,
4446 .arg4_type = ARG_CONST_SIZE
4447};
4448
4449BPF_CALL_4(bpf_lwt_seg6_action, struct sk_buff *, skb,
4450 u32, action, void *, param, u32, param_len)
4451{
4452#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4453 struct seg6_bpf_srh_state *srh_state =
4454 this_cpu_ptr(&seg6_bpf_srh_states);
4455 struct ipv6_sr_hdr *srh;
4456 int srhoff = 0;
4457 int err;
4458
4459 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4460 return -EINVAL;
4461 srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4462
4463 if (!srh_state->valid) {
4464 if (unlikely((srh_state->hdrlen & 7) != 0))
4465 return -EBADMSG;
4466
4467 srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
4468 if (unlikely(!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3)))
4469 return -EBADMSG;
4470
4471 srh_state->valid = 1;
4472 }
4473
4474 switch (action) {
4475 case SEG6_LOCAL_ACTION_END_X:
4476 if (param_len != sizeof(struct in6_addr))
4477 return -EINVAL;
4478 return seg6_lookup_nexthop(skb, (struct in6_addr *)param, 0);
4479 case SEG6_LOCAL_ACTION_END_T:
4480 if (param_len != sizeof(int))
4481 return -EINVAL;
4482 return seg6_lookup_nexthop(skb, NULL, *(int *)param);
4483 case SEG6_LOCAL_ACTION_END_B6:
4484 err = bpf_push_seg6_encap(skb, BPF_LWT_ENCAP_SEG6_INLINE,
4485 param, param_len);
4486 if (!err)
4487 srh_state->hdrlen =
4488 ((struct ipv6_sr_hdr *)param)->hdrlen << 3;
4489 return err;
4490 case SEG6_LOCAL_ACTION_END_B6_ENCAP:
4491 err = bpf_push_seg6_encap(skb, BPF_LWT_ENCAP_SEG6,
4492 param, param_len);
4493 if (!err)
4494 srh_state->hdrlen =
4495 ((struct ipv6_sr_hdr *)param)->hdrlen << 3;
4496 return err;
4497 default:
4498 return -EINVAL;
4499 }
4500#else /* CONFIG_IPV6_SEG6_BPF */
4501 return -EOPNOTSUPP;
4502#endif
4503}
4504
4505static const struct bpf_func_proto bpf_lwt_seg6_action_proto = {
4506 .func = bpf_lwt_seg6_action,
4507 .gpl_only = false,
4508 .ret_type = RET_INTEGER,
4509 .arg1_type = ARG_PTR_TO_CTX,
4510 .arg2_type = ARG_ANYTHING,
4511 .arg3_type = ARG_PTR_TO_MEM,
4512 .arg4_type = ARG_CONST_SIZE
4513};
4514
4515BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
4516 s32, len)
4517{
4518#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
4519 struct seg6_bpf_srh_state *srh_state =
4520 this_cpu_ptr(&seg6_bpf_srh_states);
4521 void *srh_end, *srh_tlvs, *ptr;
4522 struct ipv6_sr_hdr *srh;
4523 struct ipv6hdr *hdr;
4524 int srhoff = 0;
4525 int ret;
4526
4527 if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
4528 return -EINVAL;
4529 srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
4530
4531 srh_tlvs = (void *)((unsigned char *)srh + sizeof(*srh) +
4532 ((srh->first_segment + 1) << 4));
4533 srh_end = (void *)((unsigned char *)srh + sizeof(*srh) +
4534 srh_state->hdrlen);
4535 ptr = skb->data + offset;
4536
4537 if (unlikely(ptr < srh_tlvs || ptr > srh_end))
4538 return -EFAULT;
4539 if (unlikely(len < 0 && (void *)((char *)ptr - len) > srh_end))
4540 return -EFAULT;
4541
4542 if (len > 0) {
4543 ret = skb_cow_head(skb, len);
4544 if (unlikely(ret < 0))
4545 return ret;
4546
4547 ret = bpf_skb_net_hdr_push(skb, offset, len);
4548 } else {
4549 ret = bpf_skb_net_hdr_pop(skb, offset, -1 * len);
4550 }
4551
4552 bpf_compute_data_pointers(skb);
4553 if (unlikely(ret < 0))
4554 return ret;
4555
4556 hdr = (struct ipv6hdr *)skb->data;
4557 hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
4558
4559 srh_state->hdrlen += len;
4560 srh_state->valid = 0;
4561 return 0;
4562#else /* CONFIG_IPV6_SEG6_BPF */
4563 return -EOPNOTSUPP;
4564#endif
4565}
4566
4567static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
4568 .func = bpf_lwt_seg6_adjust_srh,
4569 .gpl_only = false,
4570 .ret_type = RET_INTEGER,
4571 .arg1_type = ARG_PTR_TO_CTX,
4572 .arg2_type = ARG_ANYTHING,
4573 .arg3_type = ARG_ANYTHING,
4574};
4575
4576bool bpf_helper_changes_pkt_data(void *func)
4577{
4578 if (func == bpf_skb_vlan_push ||
4579 func == bpf_skb_vlan_pop ||
4580 func == bpf_skb_store_bytes ||
4581 func == bpf_skb_change_proto ||
4582 func == bpf_skb_change_head ||
4583 func == bpf_skb_change_tail ||
4584 func == bpf_skb_adjust_room ||
4585 func == bpf_skb_pull_data ||
4586 func == bpf_clone_redirect ||
4587 func == bpf_l3_csum_replace ||
4588 func == bpf_l4_csum_replace ||
4589 func == bpf_xdp_adjust_head ||
4590 func == bpf_xdp_adjust_meta ||
4591 func == bpf_msg_pull_data ||
4592 func == bpf_xdp_adjust_tail ||
4593 func == bpf_lwt_push_encap ||
4594 func == bpf_lwt_seg6_store_bytes ||
4595 func == bpf_lwt_seg6_adjust_srh ||
4596 func == bpf_lwt_seg6_action
4597 )
4598 return true;
4599
4600 return false;
4601}
4602
4363static const struct bpf_func_proto *4603static const struct bpf_func_proto *
4364bpf_base_func_proto(enum bpf_func_id func_id)4604bpf_base_func_proto(enum bpf_func_id func_id)
4365{4605{
@@ -4774,7 +5014,6 @@ static bool lwt_is_valid_access(int off, int size, · 408 unmodified lines
4774 return bpf_skb_is_valid_access(off, size, type, prog, info);5014 return bpf_skb_is_valid_access(off, size, type, prog, info);
4775}5015}
4776 5016
4777
4778/* Attach type specific accesses */5017/* Attach type specific accesses */
4779static bool __sock_filter_check_attach_type(int off,5018static bool __sock_filter_check_attach_type(int off,
4780 enum bpf_access_type access_type,5019 enum bpf_access_type access_type,
net/ipv6/Kconfig+5−0
@@ -329,4 +329,9 @@ config IPV6_SEG6_HMAC · 328 unmodified lines
329 329
330 If unsure, say N.330 If unsure, say N.
331 331
332config IPV6_SEG6_BPF
333 def_bool y
334 depends on IPV6_SEG6_LWTUNNEL
335 depends on IPV6 = y
336
332endif # IPV6337endif # IPV6
net/ipv6/seg6_local.c+2−0
@@ -449,6 +449,8 @@ drop: · 448 unmodified lines
449 return err;449 return err;
450}450}
451 451
452DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
453
452static struct seg6_action_desc seg6_action_table[] = {454static struct seg6_action_desc seg6_action_table[] = {
453 {455 {
454 .action = SEG6_LOCAL_ACTION_END,456 .action = SEG6_LOCAL_ACTION_END,