bpf_skb_utils.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
  4. * Version: 2022-09-21
  5. */
  6. #ifndef __BPF_SKB_UTILS_H
  7. #define __BPF_SKB_UTILS_H
  8. #include <uapi/linux/bpf.h>
  9. #include <uapi/linux/if_ether.h>
  10. #include <uapi/linux/ip.h>
  11. #include <uapi/linux/ipv6.h>
  12. #include <linux/ip.h>
  13. #include <net/ipv6.h>
  14. #include <bpf/bpf_helpers.h>
  15. #include <bpf/bpf_endian.h>
  16. struct skb_parser_info {
  17. struct __sk_buff *skb;
  18. __u32 offset;
  19. int proto;
  20. };
  21. static __always_inline void *__skb_data(struct __sk_buff *skb)
  22. {
  23. return (void *)(long)READ_ONCE(skb->data);
  24. }
  25. static __always_inline void *
  26. skb_ptr(struct __sk_buff *skb, __u32 offset, __u32 len)
  27. {
  28. void *ptr = __skb_data(skb) + offset;
  29. void *end = (void *)(long)(skb->data_end);
  30. if (ptr + len >= end)
  31. return NULL;
  32. return ptr;
  33. }
  34. static __always_inline void *
  35. skb_info_ptr(struct skb_parser_info *info, __u32 len)
  36. {
  37. __u32 offset = info->offset;
  38. return skb_ptr(info->skb, offset, len);
  39. }
  40. static __always_inline void
  41. skb_parse_init(struct skb_parser_info *info, struct __sk_buff *skb)
  42. {
  43. *info = (struct skb_parser_info){
  44. .skb = skb
  45. };
  46. }
  47. static __always_inline struct ethhdr *
  48. skb_parse_ethernet(struct skb_parser_info *info)
  49. {
  50. struct ethhdr *eth;
  51. int len;
  52. len = sizeof(*eth) + 2 * sizeof(struct vlan_hdr) + sizeof(struct ipv6hdr);
  53. if (len > info->skb->len)
  54. len = info->skb->len;
  55. bpf_skb_pull_data(info->skb, len);
  56. eth = skb_info_ptr(info, sizeof(*eth));
  57. if (!eth)
  58. return NULL;
  59. info->proto = eth->h_proto;
  60. info->offset += sizeof(*eth);
  61. return eth;
  62. }
  63. static __always_inline struct vlan_hdr *
  64. skb_parse_vlan(struct skb_parser_info *info)
  65. {
  66. struct vlan_hdr *vlh;
  67. if (info->proto != bpf_htons(ETH_P_8021Q) &&
  68. info->proto != bpf_htons(ETH_P_8021AD))
  69. return NULL;
  70. vlh = skb_info_ptr(info, sizeof(*vlh));
  71. if (!vlh)
  72. return NULL;
  73. info->proto = vlh->h_vlan_encapsulated_proto;
  74. info->offset += sizeof(*vlh);
  75. return vlh;
  76. }
  77. static __always_inline struct iphdr *
  78. skb_parse_ipv4(struct skb_parser_info *info, int min_l4_bytes)
  79. {
  80. struct iphdr *iph;
  81. int proto, hdr_len;
  82. __u32 pull_len;
  83. if (info->proto != bpf_htons(ETH_P_IP))
  84. return NULL;
  85. iph = skb_info_ptr(info, sizeof(*iph));
  86. if (!iph)
  87. return NULL;
  88. hdr_len = iph->ihl * 4;
  89. hdr_len = READ_ONCE(hdr_len) & 0xff;
  90. if (hdr_len < sizeof(*iph))
  91. return NULL;
  92. pull_len = info->offset + hdr_len + min_l4_bytes;
  93. if (pull_len > info->skb->len)
  94. pull_len = info->skb->len;
  95. if (bpf_skb_pull_data(info->skb, pull_len))
  96. return NULL;
  97. iph = skb_info_ptr(info, sizeof(*iph));
  98. if (!iph)
  99. return NULL;
  100. info->proto = iph->protocol;
  101. info->offset += hdr_len;
  102. return iph;
  103. }
  104. static __always_inline struct ipv6hdr *
  105. skb_parse_ipv6(struct skb_parser_info *info, int max_l4_bytes)
  106. {
  107. struct ipv6hdr *ip6h;
  108. __u32 pull_len;
  109. if (info->proto != bpf_htons(ETH_P_IPV6))
  110. return NULL;
  111. pull_len = info->offset + sizeof(*ip6h) + max_l4_bytes;
  112. if (pull_len > info->skb->len)
  113. pull_len = info->skb->len;
  114. if (bpf_skb_pull_data(info->skb, pull_len))
  115. return NULL;
  116. ip6h = skb_info_ptr(info, sizeof(*ip6h));
  117. if (!ip6h)
  118. return NULL;
  119. info->proto = READ_ONCE(ip6h->nexthdr);
  120. info->offset += sizeof(*ip6h);
  121. return ip6h;
  122. }
  123. static __always_inline struct tcphdr *
  124. skb_parse_tcp(struct skb_parser_info *info)
  125. {
  126. struct tcphdr *tcph;
  127. if (info->proto != IPPROTO_TCP)
  128. return NULL;
  129. tcph = skb_info_ptr(info, sizeof(*tcph));
  130. if (!tcph)
  131. return NULL;
  132. info->offset += tcph->doff * 4;
  133. return tcph;
  134. }
  135. #endif