github.com/yaling888/clash@v1.53.0/component/ebpf/bpf/redir.c (about)

     1  #include <stdint.h>
     2  #include <stdbool.h>
     3  //#include <linux/types.h>
     4  
     5  #include <linux/bpf.h>
     6  #include <linux/if_ether.h>
     7  //#include <linux/if_packet.h>
     8  //#include <linux/if_vlan.h>
     9  #include <linux/ip.h>
    10  #include <linux/in.h>
    11  #include <linux/tcp.h>
    12  //#include <linux/udp.h>
    13  
    14  #include <linux/pkt_cls.h>
    15  
    16  #include "bpf_endian.h"
    17  #include "bpf_helpers.h"
    18  
    19  #define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check))
    20  #define IP_DST_OFF (ETH_HLEN + offsetof(struct iphdr, daddr))
    21  #define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr))
    22  #define IP_PROTO_OFF (ETH_HLEN + offsetof(struct iphdr, protocol))
    23  #define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check))
    24  #define TCP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, source))
    25  #define TCP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest))
    26  //#define UDP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, check))
    27  //#define UDP_SRC_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, source))
    28  //#define UDP_DST_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct udphdr, dest))
    29  #define IS_PSEUDO 0x10
    30  
    31  struct origin_info {
    32      __be32 ip;
    33      __be16 port;
    34      __u16  pad;
    35  };
    36  
    37  struct origin_info *origin_info_unused __attribute__((unused));
    38  
    39  struct redir_info {
    40      __be32 sip;
    41      __be32 dip;
    42      __be16 sport;
    43      __be16 dport;
    44  };
    45  
    46  struct redir_info *redir_info_unused __attribute__((unused));
    47  
    48  struct {
    49      __uint(type, BPF_MAP_TYPE_LRU_HASH);
    50      __type(key, struct redir_info);
    51      __type(value, struct origin_info);
    52      __uint(max_entries, 65535);
    53      __uint(pinning, LIBBPF_PIN_BY_NAME);
    54  } pair_original_dst_map SEC(".maps");
    55  
    56  struct {
    57      __uint(type, BPF_MAP_TYPE_ARRAY);
    58      __type(key, __u32);
    59      __type(value, __u32);
    60      __uint(max_entries, 3);
    61      __uint(pinning, LIBBPF_PIN_BY_NAME);
    62  } redir_params_map SEC(".maps");
    63  
    64  static __always_inline int rewrite_ip(struct __sk_buff *skb, __be32 new_ip, bool is_dest) {
    65      int ret, off = 0, flags = IS_PSEUDO;
    66      __be32 old_ip;
    67  
    68      if (is_dest)
    69          ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4);
    70      else
    71          ret = bpf_skb_load_bytes(skb, IP_SRC_OFF, &old_ip, 4);
    72  
    73      if (ret < 0) {
    74          return ret;
    75      }
    76  
    77      off = TCP_CSUM_OFF;
    78  //    __u8 proto;
    79  //
    80  //    ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1);
    81  //    if (ret < 0) {
    82  //        return BPF_DROP;
    83  //    }
    84  //
    85  //    switch (proto) {
    86  //    case IPPROTO_TCP:
    87  //        off = TCP_CSUM_OFF;
    88  //        break;
    89  //
    90  //    case IPPROTO_UDP:
    91  //        off = UDP_CSUM_OFF;
    92  //        flags |= BPF_F_MARK_MANGLED_0;
    93  //        break;
    94  //
    95  //    case IPPROTO_ICMPV6:
    96  //        off = offsetof(struct icmp6hdr, icmp6_cksum);
    97  //        break;
    98  //    }
    99  //
   100  //    if (off) {
   101      ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip, flags | sizeof(new_ip));
   102      if (ret < 0) {
   103          return ret;
   104      }
   105  //    }
   106  
   107      ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
   108      if (ret < 0) {
   109          return ret;
   110      }
   111  
   112      if (is_dest)
   113          ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0);
   114      else
   115          ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
   116  
   117      if (ret < 0) {
   118          return ret;
   119      }
   120  
   121      return 1;
   122  }
   123  
   124  static __always_inline int rewrite_port(struct __sk_buff *skb, __be16 new_port, bool is_dest) {
   125      int ret, off = 0;
   126      __be16 old_port;
   127  
   128      if (is_dest)
   129          ret = bpf_skb_load_bytes(skb, TCP_DST_OFF, &old_port, 2);
   130      else
   131          ret = bpf_skb_load_bytes(skb, TCP_SRC_OFF, &old_port, 2);
   132  
   133      if (ret < 0) {
   134          return ret;
   135      }
   136  
   137      off = TCP_CSUM_OFF;
   138  
   139      ret = bpf_l4_csum_replace(skb, off, old_port, new_port, sizeof(new_port));
   140      if (ret < 0) {
   141          return ret;
   142      }
   143  
   144      if (is_dest)
   145          ret = bpf_skb_store_bytes(skb, TCP_DST_OFF, &new_port, sizeof(new_port), 0);
   146      else
   147          ret = bpf_skb_store_bytes(skb, TCP_SRC_OFF, &new_port, sizeof(new_port), 0);
   148  
   149      if (ret < 0) {
   150          return ret;
   151      }
   152  
   153      return 1;
   154  }
   155  
   156  static __always_inline bool is_lan_ip(__be32 addr) {
   157      if (addr == 0xffffffff)
   158          return true;
   159  
   160      __u8 fist = (__u8)(addr & 0xff);
   161  
   162      if (fist == 127 || fist == 10)
   163          return true;
   164  
   165      __u8 second = (__u8)((addr >> 8) & 0xff);
   166  
   167      if (fist == 172 && second >= 16 && second <= 31)
   168          return true;
   169  
   170      if (fist == 192 && second == 168)
   171          return true;
   172  
   173      return false;
   174  }
   175  
   176  SEC("tc_clash_auto_redir_ingress")
   177  int tc_redir_ingress_func(struct __sk_buff *skb) {
   178      void *data          = (void *)(long)skb->data;
   179      void *data_end      = (void *)(long)skb->data_end;
   180      struct ethhdr *eth  = data;
   181  
   182      if ((void *)(eth + 1) > data_end)
   183          return TC_ACT_OK;
   184  
   185      if (eth->h_proto != bpf_htons(ETH_P_IP))
   186          return TC_ACT_OK;
   187  
   188      struct iphdr *iph = (struct iphdr *)(eth + 1);
   189      if ((void *)(iph + 1) > data_end)
   190          return TC_ACT_OK;
   191  
   192      __u32 key = 0, *route_index, *redir_ip, *redir_port;
   193  
   194      route_index = bpf_map_lookup_elem(&redir_params_map, &key);
   195      if (!route_index)
   196          return TC_ACT_OK;
   197  
   198      if (iph->protocol == IPPROTO_ICMP && *route_index != 0)
   199          return bpf_redirect(*route_index, 0);
   200  
   201      if (iph->protocol != IPPROTO_TCP)
   202          return TC_ACT_OK;
   203  
   204      struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
   205      if ((void *)(tcph + 1) > data_end)
   206          return TC_ACT_SHOT;
   207  
   208      key = 1;
   209      redir_ip = bpf_map_lookup_elem(&redir_params_map, &key);
   210      if (!redir_ip)
   211          return TC_ACT_OK;
   212  
   213      key = 2;
   214      redir_port = bpf_map_lookup_elem(&redir_params_map, &key);
   215      if (!redir_port)
   216          return TC_ACT_OK;
   217  
   218      __be32 new_ip   = bpf_htonl(*redir_ip);
   219      __be16 new_port = bpf_htonl(*redir_port) >> 16;
   220      __be32 old_ip   = iph->daddr;
   221      __be16 old_port = tcph->dest;
   222  
   223      if (old_ip == new_ip || is_lan_ip(old_ip) || bpf_ntohs(old_port) == 53) {
   224          return TC_ACT_OK;
   225      }
   226  
   227      struct redir_info p_key = {
   228          .sip = iph->saddr,
   229          .sport = tcph->source,
   230          .dip = new_ip,
   231          .dport = new_port,
   232      };
   233  
   234      if (tcph->syn && !tcph->ack) {
   235          struct origin_info origin = {
   236              .ip = old_ip,
   237              .port = old_port,
   238          };
   239  
   240          bpf_map_update_elem(&pair_original_dst_map, &p_key, &origin, BPF_NOEXIST);
   241  
   242          if (rewrite_ip(skb, new_ip, true) < 0) {
   243              return TC_ACT_SHOT;
   244          }
   245  
   246          if (rewrite_port(skb, new_port, true) < 0) {
   247              return TC_ACT_SHOT;
   248          }
   249      } else {
   250          struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key);
   251          if (!origin) {
   252              return TC_ACT_OK;
   253          }
   254  
   255          if (rewrite_ip(skb, new_ip, true) < 0) {
   256              return TC_ACT_SHOT;
   257          }
   258  
   259          if (rewrite_port(skb, new_port, true) < 0) {
   260              return TC_ACT_SHOT;
   261          }
   262      }
   263  
   264      return TC_ACT_OK;
   265  }
   266  
   267  SEC("tc_clash_auto_redir_egress")
   268  int tc_redir_egress_func(struct __sk_buff *skb) {
   269      void *data          = (void *)(long)skb->data;
   270      void *data_end      = (void *)(long)skb->data_end;
   271      struct ethhdr *eth  = data;
   272  
   273      if ((void *)(eth + 1) > data_end)
   274          return TC_ACT_OK;
   275  
   276      if (eth->h_proto != bpf_htons(ETH_P_IP))
   277          return TC_ACT_OK;
   278  
   279      __u32 key = 0, *redir_ip, *redir_port; // *clash_mark
   280  
   281  //    clash_mark = bpf_map_lookup_elem(&redir_params_map, &key);
   282  //    if (clash_mark && *clash_mark != 0 && *clash_mark == skb->mark)
   283  //        return TC_ACT_OK;
   284  
   285      struct iphdr *iph = (struct iphdr *)(eth + 1);
   286      if ((void *)(iph + 1) > data_end)
   287          return TC_ACT_OK;
   288  
   289      if (iph->protocol != IPPROTO_TCP)
   290          return TC_ACT_OK;
   291  
   292      struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
   293      if ((void *)(tcph + 1) > data_end)
   294          return TC_ACT_SHOT;
   295  
   296      key = 1;
   297      redir_ip = bpf_map_lookup_elem(&redir_params_map, &key);
   298      if (!redir_ip)
   299          return TC_ACT_OK;
   300  
   301      key = 2;
   302      redir_port = bpf_map_lookup_elem(&redir_params_map, &key);
   303      if (!redir_port)
   304          return TC_ACT_OK;
   305  
   306      __be32 new_ip   = bpf_htonl(*redir_ip);
   307      __be16 new_port = bpf_htonl(*redir_port) >> 16;
   308      __be32 old_ip   = iph->saddr;
   309      __be16 old_port = tcph->source;
   310  
   311      if (old_ip != new_ip || old_port != new_port) {
   312          return TC_ACT_OK;
   313      }
   314  
   315      struct redir_info p_key = {
   316          .sip = iph->daddr,
   317          .sport = tcph->dest,
   318          .dip = iph->saddr,
   319          .dport = tcph->source,
   320      };
   321  
   322      struct origin_info *origin = bpf_map_lookup_elem(&pair_original_dst_map, &p_key);
   323      if (!origin) {
   324          return TC_ACT_OK;
   325      }
   326  
   327      if (tcph->fin && tcph->ack) {
   328          bpf_map_delete_elem(&pair_original_dst_map, &p_key);
   329      }
   330  
   331      if (rewrite_ip(skb, origin->ip, false) < 0) {
   332          return TC_ACT_SHOT;
   333      }
   334  
   335      if (rewrite_port(skb, origin->port, false) < 0) {
   336          return TC_ACT_SHOT;
   337      }
   338  
   339      return TC_ACT_OK;
   340  }
   341  
   342  char _license[] SEC("license") = "GPL";