bpf_skb_utils.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
  4. */
  5. #ifndef __BPF_SKB_UTILS_H
  6. #define __BPF_SKB_UTILS_H
  7. #include <uapi/linux/bpf.h>
  8. #include <uapi/linux/if_ether.h>
  9. #include <uapi/linux/ip.h>
  10. #include <uapi/linux/ipv6.h>
  11. #include <linux/if_vlan.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. if (hdr_len < sizeof(*iph))
  90. return NULL;
  91. pull_len = info->offset + hdr_len + min_l4_bytes;
  92. if (pull_len > info->skb->len)
  93. pull_len = info->skb->len;
  94. if (bpf_skb_pull_data(info->skb, pull_len))
  95. return NULL;
  96. iph = skb_info_ptr(info, sizeof(*iph));
  97. if (!iph)
  98. return NULL;
  99. info->proto = iph->protocol;
  100. info->offset += hdr_len;
  101. return iph;
  102. }
  103. static __always_inline struct ipv6hdr *
  104. skb_parse_ipv6(struct skb_parser_info *info, int max_l4_bytes)
  105. {
  106. struct ipv6hdr *ip6h;
  107. __u32 pull_len;
  108. if (info->proto != bpf_htons(ETH_P_IPV6))
  109. return NULL;
  110. pull_len = info->offset + sizeof(*ip6h) + max_l4_bytes;
  111. if (pull_len > info->skb->len)
  112. pull_len = info->skb->len;
  113. if (bpf_skb_pull_data(info->skb, pull_len))
  114. return NULL;
  115. ip6h = skb_info_ptr(info, sizeof(*ip6h));
  116. if (!ip6h)
  117. return NULL;
  118. info->proto = READ_ONCE(ip6h->nexthdr);
  119. info->offset += sizeof(*ip6h);
  120. return ip6h;
  121. }
  122. static __always_inline struct tcphdr *
  123. skb_parse_tcp(struct skb_parser_info *info)
  124. {
  125. struct tcphdr *tcph;
  126. if (info->proto != IPPROTO_TCP)
  127. return NULL;
  128. tcph = skb_info_ptr(info, sizeof(*tcph));
  129. if (!tcph)
  130. return NULL;
  131. info->offset += tcph->doff * 4;
  132. return tcph;
  133. }
  134. #endif