From 8c65f8ff39ab7395d536a6527e955648435cf54e Mon Sep 17 00:00:00 2001 From: mzz2017 <2017@duck.com> Date: Mon, 6 Feb 2023 13:56:43 +0800 Subject: [PATCH] refactor: refactor lan tproxy using sk_assign --- common/consts/ebpf.go | 7 + component/control/control_plane.go | 64 ++-- component/control/control_plane_core.go | 19 +- component/control/kern/tproxy.c | 407 +++++++++++------------- component/control/objects.go | 1 + component/control/tcp.go | 36 +-- component/control/tproxy_utils.go | 60 ++++ component/control/udp.go | 66 ++-- component/outbound/dialer/direct.go | 33 +- component/outbound/dialer/sockopt.go | 119 +++++++ insert.sh | 8 +- 11 files changed, 492 insertions(+), 328 deletions(-) create mode 100644 component/control/tproxy_utils.go create mode 100644 component/outbound/dialer/sockopt.go diff --git a/common/consts/ebpf.go b/common/consts/ebpf.go index 9c92f41..c7c9c33 100644 --- a/common/consts/ebpf.go +++ b/common/consts/ebpf.go @@ -57,6 +57,8 @@ const ( OutboundControlPlaneDirect OutboundIndex = 0xFD OutboundLogicalOr OutboundIndex = 0xFE OutboundLogicalAnd OutboundIndex = 0xFF + + OutboundLogicalMax = OutboundLogicalAnd ) func (i OutboundIndex) String() string { @@ -102,6 +104,11 @@ var ( FtraceFeatureVersion = internal.Version{5, 5, 0} UserspaceBatchUpdateFeatureVersion = internal.Version{5, 6, 0} CgSocketCookieFeatureVersion = internal.Version{5, 7, 0} + SkAssignFeatureVersion = internal.Version{5, 7, 0} ChecksumFeatureVersion = internal.Version{5, 8, 0} UserspaceBatchUpdateLpmTrieFeatureVersion = internal.Version{5, 13, 0} ) + +const ( + TproxyMark uint32 = 0x80000000 +) diff --git a/component/control/control_plane.go b/component/control/control_plane.go index 7116627..39e4070 100644 --- a/component/control/control_plane.go +++ b/component/control/control_plane.go @@ -7,6 +7,7 @@ package control import ( "context" + "encoding/hex" "errors" "fmt" "github.com/cilium/ebpf" @@ -30,6 +31,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" ) @@ -68,15 +70,24 @@ func NewControlPlane( } // Must judge version from high to low to reduce the number of user upgrading kernel. if kernelVersion.Less(consts.ChecksumFeatureVersion) { - return nil, fmt.Errorf("your kernel version %v does not support checksum related features; expect >=%v; upgrade your kernel and try again", kernelVersion.String(), + return nil, fmt.Errorf("your kernel version %v does not support checksum related features; expect >=%v; upgrade your kernel and try again", + kernelVersion.String(), consts.ChecksumFeatureVersion.String()) } if len(wanInterface) > 0 && kernelVersion.Less(consts.CgSocketCookieFeatureVersion) { - return nil, fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again", kernelVersion.String(), + return nil, fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again", + kernelVersion.String(), consts.CgSocketCookieFeatureVersion.String()) } + if len(lanInterface) > 0 && c.kernelVersion.Less(consts.SkAssignFeatureVersion) { + return nil, fmt.Errorf("your kernel version %v does not support bind to LAN; expect >=%v; remove lan_interface in config file and try again", + c.kernelVersion.String(), + consts.SkAssignFeatureVersion.String()) + } if kernelVersion.Less(consts.BasicFeatureVersion) { - return nil, fmt.Errorf("your kernel version %v does not satisfy basic requirement; expect >=%v", c.kernelVersion.String(), consts.BasicFeatureVersion.String()) + return nil, fmt.Errorf("your kernel version %v does not satisfy basic requirement; expect >=%v", + c.kernelVersion.String(), + consts.BasicFeatureVersion.String()) } // Allow the current process to lock memory for eBPF resources. @@ -127,11 +138,11 @@ retryLoadBpf: goto retryLoadBpf } // Get detailed log from ebpf.internal.(*VerifierError) - if log.Level == logrus.PanicLevel { + if log.Level == logrus.FatalLevel { if v := reflect.Indirect(reflect.ValueOf(errors.Unwrap(errors.Unwrap(err)))); v.Kind() == reflect.Struct { if _log := v.FieldByName("Log"); _log.IsValid() { if strSlice, ok := _log.Interface().([]string); ok { - log.Panicln(strings.Join(strSlice, "\n")) + log.Fatalln(strings.Join(strSlice, "\n")) } } } @@ -306,19 +317,22 @@ retryLoadBpf: func (c *ControlPlane) ListenAndServe(port uint16) (err error) { // Listen. - listener, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(int(port))) + var listenConfig = net.ListenConfig{ + Control: func(network, address string, c syscall.RawConn) error { + return dialer.TproxyControl(c) + }, + } + tcpListener, err := listenConfig.Listen(context.TODO(), "tcp", "[::1]:"+strconv.Itoa(int(port))) if err != nil { return fmt.Errorf("listenTCP: %w", err) } - defer listener.Close() - lConn, err := net.ListenUDP("udp", &net.UDPAddr{ - IP: net.IP{0, 0, 0, 0}, - Port: int(port), - }) + defer tcpListener.Close() + packetConn, err := listenConfig.ListenPacket(context.TODO(), "udp", "[::1]:"+strconv.Itoa(int(port))) if err != nil { return fmt.Errorf("listenUDP: %w", err) } - defer lConn.Close() + defer packetConn.Close() + udpConn := packetConn.(*net.UDPConn) // Serve. @@ -334,7 +348,7 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { go func() { defer cancel() for { - lconn, err := listener.Accept() + lconn, err := tcpListener.Accept() if err != nil { if !strings.Contains(err.Error(), "use of closed network connection") { c.log.Errorf("Error when accept: %v", err) @@ -352,26 +366,30 @@ func (c *ControlPlane) ListenAndServe(port uint16) (err error) { defer cancel() for { var buf [65535]byte - n, lAddrPort, err := lConn.ReadFromUDPAddrPort(buf[:]) + var oob [120]byte // Size for original dest + n, oobn, _, src, err := udpConn.ReadMsgUDPAddrPort(buf[:], oob[:]) if err != nil { if !strings.Contains(err.Error(), "use of closed network connection") { - c.log.Errorf("ReadFromUDPAddrPort: %v, %v", lAddrPort.String(), err) + c.log.Errorf("ReadFromUDPAddrPort: %v, %v", src.String(), err) } break } - addrHdr, dataOffset, err := ParseAddrHdr(buf[:n]) - if err != nil { - c.log.Warnf("No AddrPort presented") + dst := RetrieveOriginalDest(oob[:oobn]) + if !dst.IsValid() { + c.log.WithFields(logrus.Fields{ + "source": src.String(), + "oob": hex.EncodeToString(oob[:oobn]), + }).Warnf("Failed to retrieve original dest") continue } - newBuf := pool.Get(n - dataOffset) - copy(newBuf, buf[dataOffset:n]) - go func(data []byte, lConn *net.UDPConn, lAddrPort netip.AddrPort, addrHdr *AddrHdr) { - if e := c.handlePkt(newBuf, lConn, lAddrPort, addrHdr); e != nil { + newBuf := pool.Get(n) + copy(newBuf, buf[:n]) + go func(data []byte, src, dst netip.AddrPort) { + if e := c.handlePkt(newBuf, src, dst); e != nil { c.log.Warnln("handlePkt:", e) } pool.Put(newBuf) - }(newBuf, lConn, lAddrPort, addrHdr) + }(newBuf, src, dst) } }() <-ctx.Done() diff --git a/component/control/control_plane_core.go b/component/control/control_plane_core.go index fcb9f83..cda5237 100644 --- a/component/control/control_plane_core.go +++ b/component/control/control_plane_core.go @@ -18,6 +18,7 @@ import ( "golang.org/x/sys/unix" "net/netip" "os" + "os/exec" "regexp" ) @@ -115,7 +116,23 @@ func (c *ControlPlaneCore) BindLan(ifname string) error { if err != nil { return err } - + /// Insert ip rule / ip route. + if err = exec.Command("sh", "-c", ` + ip rule add fwmark 0x80000000/0x80000000 table 2023 + ip route add local 0.0.0.0/0 dev lo table 2023 + ip -6 rule add fwmark 0x80000000/0x80000000 table 2023 + ip -6 route add local ::/0 dev lo table 2023 +`).Run(); err != nil { + return err + } + c.deferFuncs = append(c.deferFuncs, func() error { + return exec.Command("sh", "-c", ` + ip rule del fwmark 0x80000000/0x80000000 table 2023 + ip route del local 0.0.0.0/0 dev lo table 2023 + ip -6 rule del fwmark 0x80000000/0x80000000 table 2023 + ip -6 route del local ::/0 dev lo table 2023 +`).Run() + }) /// Insert an elem into IfindexParamsMap. ifParams, err := getifParamsFromLink(link) if err != nil { diff --git a/component/control/kern/tproxy.c b/component/control/kern/tproxy.c index db57d60..c4890b3 100644 --- a/component/control/kern/tproxy.c +++ b/component/control/kern/tproxy.c @@ -41,6 +41,7 @@ #define NOWHERE_IFINDEX 0 #define LOOPBACK_IFINDEX 1 +#define LOOPBACK_ADDR 0x7f000001 #define MAX_PARAM_LEN 16 #define MAX_INTERFACE_NUM 128 @@ -61,6 +62,12 @@ #define OUTBOUND_LOGICAL_AND 0xFF #define OUTBOUND_LOGICAL_MASK 0xFE +#define TPROXY_MARK 0x80000000 + +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ + +enum { BPF_F_CURRENT_NETNS = -1 }; + enum { DisableL4ChecksumPolicy_EnableL4Checksum, DisableL4ChecksumPolicy_Restore, @@ -88,6 +95,12 @@ struct ip_port_outbound { __u8 unused; }; +struct tuples { + struct ip_port src; + struct ip_port dst; + __u8 l4proto; +}; + /// TODO: Remove items from the dst_map by conntrack. // Dest map: struct { @@ -103,6 +116,13 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } tcp_dst_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, struct tuples); + __type(value, __u32); // outbound + __uint(max_entries, MAX_DST_MAPPING_NUM); +} routing_tuples_map SEC(".maps"); + // Params: struct { __uint(type, BPF_MAP_TYPE_ARRAY); @@ -575,7 +595,8 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, __u8 *l4proto) { __u32 offset = 0; - int ret = bpf_skb_load_bytes(skb, offset, ethh, sizeof(struct ethhdr)); + int ret; + ret = bpf_skb_load_bytes(skb, offset, ethh, sizeof(struct ethhdr)); if (ret) { bpf_printk("not ethernet packet"); return 1; @@ -583,8 +604,8 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, // Skip ethhdr for next hdr. offset += sizeof(struct ethhdr); - *ihl = 0; *ipversion = 0; + *ihl = 0; *l4proto = 0; // bpf_printk("parse_transport: h_proto: %u ? %u %u", eth->h_proto, @@ -635,8 +656,9 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, return handle_ipv6_extensions(skb, offset, ipv6h->nexthdr, tcph, udph, ihl, l4proto); + } else { + return 1; } - return 1; } static __always_inline int @@ -913,7 +935,7 @@ static __always_inline int decap_after_udp_hdr(struct __sk_buff *skb, // Do not use __always_inline here because this function is too heavy. static int __attribute__((noinline)) -routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], +routing(const __u32 flag[6], const void *l4hdr, const __be32 saddr[4], const __be32 _daddr[4], const __be32 mac[4]) { #define _l4proto_type flag[0] #define _ipversion_type flag[1] @@ -938,11 +960,11 @@ routing(const __u32 flag[6], const void *l4_hdr, const __be32 saddr[4], __u16 h_dport; __u16 h_sport; if (_l4proto_type == L4ProtoType_TCP) { - h_dport = bpf_ntohs(((struct tcphdr *)l4_hdr)->dest); - h_sport = bpf_ntohs(((struct tcphdr *)l4_hdr)->source); + h_dport = bpf_ntohs(((struct tcphdr *)l4hdr)->dest); + h_sport = bpf_ntohs(((struct tcphdr *)l4hdr)->source); } else { - h_dport = bpf_ntohs(((struct udphdr *)l4_hdr)->dest); - h_sport = bpf_ntohs(((struct udphdr *)l4_hdr)->source); + h_dport = bpf_ntohs(((struct udphdr *)l4hdr)->dest); + h_sport = bpf_ntohs(((struct udphdr *)l4hdr)->source); } key = MatchType_SourcePort; @@ -1153,11 +1175,9 @@ int tproxy_lan_ingress(struct __sk_buff *skb) { struct ipv6hdr ipv6h; struct tcphdr tcph; struct udphdr udph; - // __sum16 bak_cksm = 0; __u8 ihl; __u8 ipversion; __u8 l4proto; - bool tcp_state_syn; int ret = parse_transport(skb, ðh, &iph, &ipv6h, &tcph, &udph, &ihl, &ipversion, &l4proto); if (ret) { @@ -1165,216 +1185,182 @@ int tproxy_lan_ingress(struct __sk_buff *skb) { return TC_ACT_OK; } - // Backup for further use. - __be16 ipv4_tot_len = 0; - - // Parse saddr and daddr as ipv6 format. - __be32 saddr[4]; - __be32 daddr[4]; + // Prepare five tuples. + struct tuples tuples = {0}; + tuples.l4proto = l4proto; if (ipversion == 4) { - saddr[0] = 0; - saddr[1] = 0; - saddr[2] = bpf_htonl(0x0000ffff); - saddr[3] = iph.saddr; + tuples.src.ip[2] = bpf_htonl(0x0000ffff); + tuples.src.ip[3] = iph.saddr; - daddr[0] = 0; - daddr[1] = 0; - daddr[2] = bpf_htonl(0x0000ffff); - daddr[3] = iph.daddr; + tuples.dst.ip[2] = bpf_htonl(0x0000ffff); + tuples.dst.ip[3] = iph.daddr; - ipv4_tot_len = iph.tot_len; } else { - __builtin_memcpy(daddr, &ipv6h.daddr, IPV6_BYTE_LENGTH); - __builtin_memcpy(saddr, &ipv6h.saddr, IPV6_BYTE_LENGTH); + __builtin_memcpy(tuples.dst.ip, &ipv6h.daddr, IPV6_BYTE_LENGTH); + __builtin_memcpy(tuples.src.ip, &ipv6h.saddr, IPV6_BYTE_LENGTH); + } + if (l4proto == IPPROTO_TCP) { + tuples.src.port = tcph.source; + tuples.dst.port = tcph.dest; + } else { + tuples.src.port = udph.source; + tuples.dst.port = udph.dest; } - __u32 ifindex = skb->ifindex; - struct if_params *ifparams = - bpf_map_lookup_elem(&ifindex_params_map, &ifindex); - if (unlikely(!ifparams)) { - return -1; - } - // Never disable checksum in rx. - bool disable_checksum = false; + /** + ip rule add fwmark 0x80000000/0x80000000 table 1000 + ip route add local 0.0.0.0/0 dev lo table 1000 + ip -6 rule add fwmark 0x80000000/0x80000000 table 1000 + ip -6 route add local ::/0 dev lo table 1000 - // If this packet is sent to this host and not a DNS packet, accept it. - __u32 tproxy_ip[4]; - int to_host = ip_is_host(ipversion, ifparams, daddr, tproxy_ip); - if (to_host < 0) { // error - // bpf_printk("to_host: %ld", to_host); - return TC_ACT_OK; - } - if (to_host == 1) { - if (l4proto == IPPROTO_UDP && udph.dest == 53) { - // To udp:host:53. Process it. - } else { - // To host. Accept. - return TC_ACT_OK; - } + ip rule del fwmark 0x80000000/0x80000000 table 1000 + ip route del local 0.0.0.0/0 dev lo table 1000 + ip -6 rule del fwmark 0x80000000/0x80000000 table 1000 + ip -6 route del local ::/0 dev lo table 1000 + */ + struct bpf_sock_tuple tuple = {0}; + __u32 tuple_size; + if (ipversion == 4) { + tuple.ipv4.daddr = tuples.dst.ip[3]; + tuple.ipv4.saddr = tuples.src.ip[3]; + tuple.ipv4.dport = tuples.dst.port; + tuple.ipv4.sport = tuples.src.port; + tuple_size = sizeof(tuple.ipv4); + } else { + __builtin_memcpy(tuple.ipv6.daddr, tuples.dst.ip, IPV6_BYTE_LENGTH); + __builtin_memcpy(tuple.ipv6.saddr, tuples.src.ip, IPV6_BYTE_LENGTH); + tuple.ipv6.dport = tuples.dst.port; + tuple.ipv6.sport = tuples.src.port; + tuple_size = sizeof(tuple.ipv6); } - __be16 *tproxy_port = bpf_map_lookup_elem(¶m_map, &tproxy_port_key); - if (!tproxy_port) { - return TC_ACT_OK; - } + struct bpf_sock *sk; + bool is_old_conn = false; if (l4proto == IPPROTO_TCP) { - // Backup for further use. - // bak_cksm = tcph.check; - tcp_state_syn = tcph.syn && !tcph.ack; - struct ip_port key_src; - __builtin_memset(&key_src, 0, sizeof(key_src)); - __builtin_memcpy(key_src.ip, saddr, IPV6_BYTE_LENGTH); - key_src.port = tcph.source; - __u8 outbound; - if (unlikely(tcp_state_syn)) { - // New TCP connection. - // bpf_printk("[%X]New Connection", bpf_ntohl(tcph.seq)); - __u32 flag[6] = {L4ProtoType_TCP}; // TCP - if (ipversion == 6) { - flag[1] = IpVersionType_6; - } else { - flag[1] = IpVersionType_4; - } - __be32 mac[4] = { - 0, - 0, - bpf_htonl((ethh.h_source[0] << 8) + (ethh.h_source[1])), - bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + - (ethh.h_source[4] << 8) + (ethh.h_source[5])), - }; - if ((ret = routing(flag, &tcph, saddr, daddr, mac)) < 0) { - bpf_printk("shot routing: %d", ret); - return TC_ACT_SHOT; - } + // TCP. - outbound = ret; -#if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT) - // Print only new connection. - bpf_printk("tcp(lan): outbound: %u, %pI6:%u", outbound, daddr, - bpf_ntohs(key_src.port)); -#endif - } else { - // bpf_printk("[%X]Old Connection", bpf_ntohl(tcph.seq)); - // The TCP connection exists. - struct ip_port_outbound *dst = - bpf_map_lookup_elem(&tcp_dst_map, &key_src); - if (!dst) { - // Do not impact previous connections. - return TC_ACT_OK; + sk = bpf_skc_lookup_tcp(skb, &tuple, tuple_size, BPF_F_CURRENT_NETNS, 0); + if (sk) { + if (sk->state != BPF_TCP_LISTEN) { + // Old connection. + is_old_conn = true; + goto assign; } - outbound = dst->outbound; + bpf_sk_release(sk); } + } else { + // UDP. - if (outbound == OUTBOUND_DIRECT) { - return TC_ACT_OK; - } else if (unlikely(outbound == OUTBOUND_BLOCK)) { - return TC_ACT_SHOT; - } else { - // Rewrite to control plane. - - if (unlikely(tcp_state_syn)) { - struct ip_port_outbound value_dst; - __builtin_memcpy(value_dst.ip, daddr, IPV6_BYTE_LENGTH); - value_dst.port = tcph.dest; - value_dst.outbound = outbound; - bpf_map_update_elem(&tcp_dst_map, &key_src, &value_dst, BPF_ANY); - } - - __u32 *dst_ip = daddr; - __u16 dst_port = tcph.dest; - if ((ret = rewrite_ip(skb, ipversion, IPPROTO_TCP, ihl, dst_ip, tproxy_ip, - true, !disable_checksum))) { - bpf_printk("Shot IP: %d", ret); - return TC_ACT_SHOT; - } - if ((ret = rewrite_port(skb, IPPROTO_TCP, ihl, dst_port, *tproxy_port, - true, !disable_checksum))) { - bpf_printk("Shot Port: %d", ret); - return TC_ACT_SHOT; - } - } - } else if (l4proto == IPPROTO_UDP) { - // Backup for further use. - // bak_cksm = udph.check; - struct ip_port_outbound new_hdr; - __builtin_memset(&new_hdr, 0, sizeof(new_hdr)); - __builtin_memcpy(new_hdr.ip, daddr, IPV6_BYTE_LENGTH); - new_hdr.port = udph.dest; - - // Routing. It decides if we redirect traffic to control plane. - __u32 flag[6] = {L4ProtoType_UDP}; - if (ipversion == 6) { - flag[1] = IpVersionType_6; - } else { - flag[1] = IpVersionType_4; - } - __be32 mac[4] = { - 0, - 0, - bpf_htonl((ethh.h_source[0] << 8) + (ethh.h_source[1])), - bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + - (ethh.h_source[4] << 8) + (ethh.h_source[5])), - }; - if ((ret = routing(flag, &udph, saddr, daddr, mac)) < 0) { - bpf_printk("shot routing: %d", ret); - return TC_ACT_SHOT; - } - new_hdr.outbound = ret; - -#if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT) - bpf_printk("udp(lan): outbound: %u, %pI6:%u", new_hdr.outbound, daddr, - bpf_ntohs(new_hdr.port)); -#endif - - if (new_hdr.outbound == OUTBOUND_DIRECT) { - return TC_ACT_OK; - } else if (unlikely(new_hdr.outbound == OUTBOUND_BLOCK)) { - return TC_ACT_SHOT; - } else { - // Rewrite to control plane. - - // Encap a header to transmit fullcone tuple. - if ((ret = - encap_after_udp_hdr(skb, ipversion, ihl, ipv4_tot_len, &new_hdr, - sizeof(new_hdr), !disable_checksum))) { - return TC_ACT_SHOT; - } - - // Rewrite udp dst ip. - // bpf_printk("rewrite dst ip from %pI4", &ori_dst.ip); - if ((ret = rewrite_ip(skb, ipversion, IPPROTO_UDP, ihl, new_hdr.ip, - tproxy_ip, true, !disable_checksum))) { - bpf_printk("Shot IP: %d", ret); - return TC_ACT_SHOT; - } - - // Rewrite udp dst port. - if ((ret = rewrite_port(skb, IPPROTO_UDP, ihl, new_hdr.port, *tproxy_port, - true, !disable_checksum))) { - bpf_printk("Shot Port: %d", ret); - return TC_ACT_SHOT; - } + sk = bpf_sk_lookup_udp(skb, &tuple, tuple_size, BPF_F_CURRENT_NETNS, 0); + if (sk) { + goto assign; } } - // Print packet in hex for debugging (checksum or something else). - // bpf_printk("DEBUG"); - // for (__u32 i = 0; i < skb->len && i < 200; i++) { - // __u8 t = 0; - // bpf_skb_load_bytes(skb, i, &t, 1); - // bpf_printk("%02x", t); - // } + // Routing for new connection. + __u32 flag[6] = {0}; // TCP + void *l4hdr; + if (l4proto == IPPROTO_TCP) { + l4hdr = &tcph; + flag[0] = L4ProtoType_TCP; + } else { + l4hdr = &udph; + flag[0] = L4ProtoType_UDP; + } + if (ipversion == 4) { + flag[1] = IpVersionType_4; + } else { + flag[1] = IpVersionType_6; + } + __be32 mac[4] = { + 0, + 0, + bpf_htonl((ethh.h_source[0] << 8) + (ethh.h_source[1])), + bpf_htonl((ethh.h_source[2] << 24) + (ethh.h_source[3] << 16) + + (ethh.h_source[4] << 8) + (ethh.h_source[5])), + }; + if ((ret = routing(flag, l4hdr, tuples.src.ip, tuples.dst.ip, mac)) < 0) { + bpf_printk("shot routing: %d", ret); + return TC_ACT_SHOT; + } + __u32 outbound = ret; +#if defined(__DEBUG_ROUTING) || defined(__PRINT_ROUTING_RESULT) + if (l4proto == IPPROTO_TCP) { + bpf_printk("tcp(lan): outbound: %u, target: %pI6:%u", outbound, + tuples.dst.ip, bpf_ntohs(tuples.dst.port)); + } else { + bpf_printk("udp(lan): outbound: %u, target: %pI6:%u", outbound, + tuples.dst.ip, bpf_ntohs(tuples.dst.port)); + } +#endif + if (outbound == OUTBOUND_DIRECT) { + goto direct; + } else if (unlikely(outbound == OUTBOUND_BLOCK)) { + goto block; + } - // Disable checksum. - if (disable_checksum) { - // Set checksum zero. - __u32 l4_cksm_off = l4_checksum_off(l4proto, ihl); - __sum16 bak_cksm = 0; - bpf_skb_store_bytes(skb, l4_cksm_off, &bak_cksm, sizeof(bak_cksm), 0); - bpf_csum_level(skb, BPF_CSUM_LEVEL_RESET); + // Save routing result. + if ((ret = bpf_map_update_elem(&routing_tuples_map, &tuples, &outbound, + BPF_ANY))) { + bpf_printk("shot save routing result: %d", ret); + return TC_ACT_SHOT; + } + + // Assign to control plane. + __be16 *tproxy_port = bpf_map_lookup_elem(¶m_map, &tproxy_port_key); + if (!tproxy_port) { + bpf_printk("shot tproxy port not set: %d", ret); + return TC_ACT_SHOT; + } + __builtin_memset(&tuple, 0, sizeof(tuple)); + tuple.ipv6.daddr[3] = bpf_htonl(0x00000001); + tuple.ipv6.dport = *tproxy_port; + + if (l4proto == IPPROTO_TCP) { + // TCP. + + sk = bpf_skc_lookup_tcp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0); + if (!sk || sk->state != BPF_TCP_LISTEN) { + bpf_printk("shot tproxy not listen: %d", ret); + goto sk_shot; + } + } else { + // UDP. + + sk = bpf_sk_lookup_udp(skb, &tuple, sizeof(tuple), BPF_F_CURRENT_NETNS, 0); + if (!sk) { + goto sk_shot; + } + } + +assign: + skb->mark = TPROXY_MARK; + ret = bpf_sk_assign(skb, sk, 0); + bpf_sk_release(sk); + if (ret) { + if (is_old_conn && ret == -ESOCKTNOSUPPORT) { + bpf_printk("bpf_sk_assign: %d, perhaps you have other TPROXY programs " + "(such as v2ray) running?", + ret); + } else { + bpf_printk("bpf_sk_assign: %d", ret); + } + return TC_ACT_SHOT; } return TC_ACT_OK; + +sk_shot: + if (sk) { + bpf_sk_release(sk); + } + return TC_ACT_SHOT; + +direct: + return TC_ACT_OK; + +block: + return TC_ACT_SHOT; } // Cookie will change after the first packet, so we just use it for @@ -2115,7 +2101,6 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) { for (loc = 0, j = 0; j < MAX_ARG_LEN_TO_PROBE; ++j, loc = ((loc + 1) & (MAX_ARG_SCANNER_BUFFER_SIZE - 1))) { // volatile unsigned long k = j; // Cheat to unroll. - // if (arg_start + k >= arg_end) { if (unlikely(arg_start + j >= arg_end)) { break; } @@ -2129,6 +2114,7 @@ static int __always_inline update_map_elem_by_cookie(const __u64 cookie) { } else { buf[to_read] = 0; } + // No need to CO-RE. if ((ret = bpf_probe_read_user(&buf, to_read, (const void *)(arg_start + j)))) { bpf_printk("failed to read process name: %d", ret); @@ -2184,25 +2170,4 @@ int tproxy_wan_cg_sock_release(struct bpf_sock *sk) { return 1; } -// SEC("cgroup/connect4") -// int tproxy_wan_cg_connect4(struct bpf_sock_addr *ctx) { -// update_map_elem_by_cookie(bpf_get_socket_cookie(ctx)); -// return 1; -// } -// SEC("cgroup/connect6") -// int tproxy_wan_cg_connect6(struct bpf_sock_addr *ctx) { -// update_map_elem_by_cookie(bpf_get_socket_cookie(ctx)); -// return 1; -// } -// SEC("cgroup/sendmsg4") -// int tproxy_wan_cg_sendmsg4(struct bpf_sock_addr *ctx) { -// update_map_elem_by_cookie(bpf_get_socket_cookie(ctx)); -// return 1; -// } -// SEC("cgroup/sendmsg6") -// int tproxy_wan_cg_sendmsg6(struct bpf_sock_addr *ctx) { -// update_map_elem_by_cookie(bpf_get_socket_cookie(ctx)); -// return 1; -// } - SEC("license") const char __license[] = "Dual BSD/GPL"; diff --git a/component/control/objects.go b/component/control/objects.go index 04758c7..f222dbd 100644 --- a/component/control/objects.go +++ b/component/control/objects.go @@ -70,6 +70,7 @@ func generate(output string) error { func GenerateObjects(output string) { if err := generate(output); err != nil { + fmt.Println(err.Error()) os.Exit(1) } } diff --git a/component/control/tcp.go b/component/control/tcp.go index 00c930b..553d5bf 100644 --- a/component/control/tcp.go +++ b/component/control/tcp.go @@ -9,48 +9,36 @@ import ( "fmt" "github.com/mzz2017/softwind/pkg/zeroalloc/io" "github.com/sirupsen/logrus" - "github.com/v2rayA/dae/common" "github.com/v2rayA/dae/common/consts" - internal "github.com/v2rayA/dae/pkg/ebpf_internal" + "golang.org/x/sys/unix" "net" - "net/netip" "strings" "time" ) func (c *ControlPlane) handleConn(lConn net.Conn) (err error) { defer lConn.Close() - rAddr := lConn.RemoteAddr().(*net.TCPAddr).AddrPort() - ip6 := rAddr.Addr().As16() - - var value bpfIpPortOutbound - if err := c.bpf.TcpDstMap.Lookup(bpfIpPort{ - Ip: common.Ipv6ByteSliceToUint32Array(ip6[:]), - Port: internal.Htons(rAddr.Port()), - }, &value); err != nil { - return fmt.Errorf("reading map: key %v: %w", rAddr.String(), err) + src := lConn.RemoteAddr().(*net.TCPAddr).AddrPort() + dst := lConn.LocalAddr().(*net.TCPAddr).AddrPort() + outboundIndex, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_TCP) + if err != nil { + return fmt.Errorf("RetrieveOutboundIndex: %w", err) } - dstSlice, ok := netip.AddrFromSlice(common.Ipv6Uint32ArrayToByteSlice(value.Ip)) - if !ok { - return fmt.Errorf("failed to parse dest ip: %v", value.Ip) - } - dst := netip.AddrPortFrom(dstSlice, internal.Htons(value.Port)) - switch consts.OutboundIndex(value.Outbound) { + switch consts.OutboundIndex(outboundIndex) { case consts.OutboundDirect: case consts.OutboundControlPlaneDirect: - value.Outbound = uint8(consts.OutboundDirect) + outboundIndex = consts.OutboundDirect c.log.Tracef("outbound: %v => %v", consts.OutboundControlPlaneDirect.String(), - consts.OutboundIndex(value.Outbound).String(), + consts.OutboundIndex(outboundIndex).String(), ) default: } - outbound := c.outbounds[value.Outbound] + outbound := c.outbounds[outboundIndex] // TODO: Set-up ip to domain mapping and show domain if possible. - src := lConn.RemoteAddr().(*net.TCPAddr).AddrPort() - if value.Outbound < 0 || int(value.Outbound) >= len(c.outbounds) { - return fmt.Errorf("outbound id from bpf is out of range: %v not in [0, %v]", value.Outbound, len(c.outbounds)-1) + if outboundIndex < 0 || int(outboundIndex) >= len(c.outbounds) { + return fmt.Errorf("outbound id from bpf is out of range: %v not in [0, %v]", outboundIndex, len(c.outbounds)-1) } dialer, err := outbound.Select() if err != nil { diff --git a/component/control/tproxy_utils.go b/component/control/tproxy_utils.go new file mode 100644 index 0000000..af68f59 --- /dev/null +++ b/component/control/tproxy_utils.go @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) since 2023, mzz2017 + */ + +package control + +import ( + "encoding/binary" + "fmt" + "github.com/v2rayA/dae/common" + "github.com/v2rayA/dae/common/consts" + internal "github.com/v2rayA/dae/pkg/ebpf_internal" + "golang.org/x/sys/unix" + "net/netip" + "syscall" +) + +func (c *ControlPlaneCore) RetrieveOutboundIndex(src, dst netip.AddrPort, l4proto uint8) (consts.OutboundIndex, error) { + srcIp6 := src.Addr().As16() + dstIp6 := dst.Addr().As16() + + var outboundIndex uint32 + if err := c.bpf.RoutingTuplesMap.Lookup(bpfTuples{ + Src: bpfIpPort{ + Ip: common.Ipv6ByteSliceToUint32Array(srcIp6[:]), + Port: internal.Htons(src.Port()), + }, + Dst: bpfIpPort{ + Ip: common.Ipv6ByteSliceToUint32Array(dstIp6[:]), + Port: internal.Htons(dst.Port()), + }, + L4proto: l4proto, + }, &outboundIndex); err != nil { + return 0, fmt.Errorf("reading map: key %v: %w", src.String(), err) + } + if outboundIndex > uint32(consts.OutboundLogicalMax) { + return 0, fmt.Errorf("bad outbound index") + } + return consts.OutboundIndex(outboundIndex), nil +} + +func RetrieveOriginalDest(oob []byte) netip.AddrPort { + msgs, err := syscall.ParseSocketControlMessage(oob) + if err != nil { + return netip.AddrPort{} + } + for _, msg := range msgs { + if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR { + ip := msg.Data[4:8] + port := binary.BigEndian.Uint16(msg.Data[2:4]) + return netip.AddrPortFrom(netip.AddrFrom4(*(*[4]byte)(ip)), port) + } else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == unix.IPV6_RECVORIGDSTADDR { + ip := msg.Data[8:24] + port := binary.BigEndian.Uint16(msg.Data[2:4]) + return netip.AddrPortFrom(netip.AddrFrom4(*(*[4]byte)(ip)), port) + } + } + return netip.AddrPort{} +} diff --git a/component/control/udp.go b/component/control/udp.go index bea6f84..6c330e3 100644 --- a/component/control/udp.go +++ b/component/control/udp.go @@ -14,9 +14,11 @@ import ( "github.com/v2rayA/dae/common/consts" "github.com/v2rayA/dae/component/outbound/dialer" "golang.org/x/net/dns/dnsmessage" + "golang.org/x/sys/unix" "net" "net/netip" "strings" + "syscall" "time" ) @@ -80,7 +82,20 @@ func sendPktWithHdr(data []byte, from netip.AddrPort, lConn *net.UDPConn, to net return err } -func (c *ControlPlane) RelayToUDP(lConn *net.UDPConn, to netip.AddrPort, isDNS bool, dummyFrom *netip.AddrPort, validateRushAns bool) UdpHandler { +func sendPktBind(data []byte, from netip.AddrPort, to netip.AddrPort) error { + d := net.Dialer{Control: func(network, address string, c syscall.RawConn) error { + return dialer.BindControl(c, from) + }} + conn, err := d.Dial("udp", to.String()) + if err != nil { + return err + } + uConn := conn.(*net.UDPConn) + _, err = uConn.Write(data) + return err +} + +func (c *ControlPlane) RelayToUDP(to netip.AddrPort, isDNS bool, dummyFrom *netip.AddrPort, validateRushAns bool) UdpHandler { return func(data []byte, from netip.AddrPort) (err error) { // Do not return conn-unrelated err in this func. @@ -103,52 +118,57 @@ func (c *ControlPlane) RelayToUDP(lConn *net.UDPConn, to netip.AddrPort, isDNS b if dummyFrom != nil { from = *dummyFrom } - return sendPktWithHdr(data, from, lConn, to) + + return sendPktBind(data, from, to) } } -func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort netip.AddrPort, addrHdr *AddrHdr) (err error) { - switch consts.OutboundIndex(addrHdr.Outbound) { +func (c *ControlPlane) handlePkt(data []byte, src, dst netip.AddrPort) (err error) { + outboundIndex, err := c.RetrieveOutboundIndex(src, dst, unix.IPPROTO_UDP) + if err != nil { + return fmt.Errorf("RetrieveOutboundIndex: %w", err) + } + switch outboundIndex { case consts.OutboundDirect: case consts.OutboundControlPlaneDirect: - addrHdr.Outbound = uint8(consts.OutboundDirect) + outboundIndex = consts.OutboundDirect c.log.Tracef("outbound: %v => %v", consts.OutboundControlPlaneDirect.String(), - consts.OutboundIndex(addrHdr.Outbound).String(), + outboundIndex.String(), ) default: } - if int(addrHdr.Outbound) >= len(c.outbounds) { - return fmt.Errorf("outbound %v out of range", addrHdr.Outbound) + if int(outboundIndex) >= len(c.outbounds) { + return fmt.Errorf("outbound %v out of range", outboundIndex) } - outbound := c.outbounds[addrHdr.Outbound] + outbound := c.outbounds[outboundIndex] dnsMessage, natTimeout := ChooseNatTimeout(data) // We should cache DNS records and set record TTL to 0, in order to monitor the dns req and resp in real time. isDns := dnsMessage != nil var dummyFrom *netip.AddrPort - dest := addrHdr.Dest + destToSend := dst if isDns { if resp := c.LookupDnsRespCache(dnsMessage); resp != nil { // Send cache to client directly. - if err = sendPktWithHdr(resp, dest, lConn, lAddrPort); err != nil { + if err = sendPktBind(resp, destToSend, src); err != nil { return fmt.Errorf("failed to write cached DNS resp: %w", err) } if c.log.IsLevelEnabled(logrus.DebugLevel) && len(dnsMessage.Questions) > 0 { q := dnsMessage.Questions[0] c.log.Tracef("UDP(DNS) %v <-[%v]-> Cache: %v %v", - RefineSourceToShow(lAddrPort, dest.Addr()), outbound.Name, strings.ToLower(q.Name.String()), q.Type, + RefineSourceToShow(src, destToSend.Addr()), outbound.Name, strings.ToLower(q.Name.String()), q.Type, ) } return nil } // Need to make a DNS request. - c.log.Tracef("Modify dns target %v to upstream: %v", RefineAddrPortToShow(dest), c.dnsUpstream) + c.log.Tracef("Modify dns target %v to upstream: %v", RefineAddrPortToShow(destToSend), c.dnsUpstream) // Modify dns target to upstream. // NOTICE: Routing was calculated in advance by the eBPF program. - dummyFrom = &addrHdr.Dest - dest = c.dnsUpstream + dummyFrom = &dst + destToSend = c.dnsUpstream // Flip dns question to reduce dns pollution. FlipDnsQuestionCase(dnsMessage) @@ -166,9 +186,9 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti // We only validate rush-ans when outbound is direct and pkt does not send to a home device. // Because additional record OPT may not be supported by home router. // So se should trust home devices even if they make rush-answer (or looks like). - validateRushAns := addrHdr.Outbound == uint8(consts.OutboundDirect) && !dest.Addr().IsPrivate() - ue, err := DefaultUdpEndpointPool.GetOrCreate(lAddrPort, &UdpEndpointOptions{ - Handler: c.RelayToUDP(lConn, lAddrPort, isDns, dummyFrom, validateRushAns), + validateRushAns := outboundIndex == consts.OutboundDirect && !destToSend.Addr().IsPrivate() + ue, err := DefaultUdpEndpointPool.GetOrCreate(src, &UdpEndpointOptions{ + Handler: c.RelayToUDP(src, isDns, dummyFrom, validateRushAns), NatTimeout: natTimeout, DialerFunc: func() (*dialer.Dialer, error) { newDialer, err := outbound.Select() @@ -177,7 +197,7 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti } return newDialer, nil }, - Target: dest, + Target: destToSend, }) if err != nil { return fmt.Errorf("failed to GetOrCreate: %w", err) @@ -194,7 +214,7 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti "qname": strings.ToLower(q.Name.String()), "qtype": q.Type, }).Infof("%v <-> %v", - RefineSourceToShow(lAddrPort, dest.Addr()), RefineAddrPortToShow(dest), + RefineSourceToShow(src, destToSend.Addr()), RefineAddrPortToShow(destToSend), ) } else { // TODO: Set-up ip to domain mapping and show domain if possible. @@ -203,11 +223,11 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti "outbound": outbound.Name, "dialer": d.Name(), }).Infof("%v <-> %v", - RefineSourceToShow(lAddrPort, dest.Addr()), RefineAddrPortToShow(dest), + RefineSourceToShow(src, destToSend.Addr()), RefineAddrPortToShow(destToSend), ) } - //log.Printf("WriteToUDPAddrPort->%v", dest) - _, err = ue.WriteToUDPAddrPort(data, dest) + //log.Printf("WriteToUDPAddrPort->%v", destToSend) + _, err = ue.WriteToUDPAddrPort(data, destToSend) if err != nil { return fmt.Errorf("failed to write UDP packet req: %w", err) } diff --git a/component/outbound/dialer/direct.go b/component/outbound/dialer/direct.go index 715cb5d..db761aa 100644 --- a/component/outbound/dialer/direct.go +++ b/component/outbound/dialer/direct.go @@ -3,8 +3,6 @@ package dialer import ( "golang.org/x/net/proxy" "net" - "runtime" - "syscall" ) var SymmetricDirect = newDirect(false) @@ -26,9 +24,7 @@ type direct struct { func newDirect(fullCone bool) proxy.Dialer { return &direct{ - netDialer: &net.Dialer{Control: func(network, address string, c syscall.RawConn) error { - return SoMarkControl(c) - }}, + netDialer: &net.Dialer{}, fullCone: fullCone, } } @@ -44,10 +40,6 @@ func (d *direct) Dial(network, addr string) (c net.Conn, err error) { if err != nil { return nil, err } - raw, e := conn.SyscallConn() - if e == nil { - _ = SoMarkControl(raw) - } return &directUDPConn{UDPConn: conn, FullCone: true}, nil } else { conn, err := d.netDialer.Dial(network, addr) @@ -88,26 +80,3 @@ func (c *directUDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { } return c.UDPConn.WriteToUDP(b, addr) } - -var fwmarkIoctl int - -func init() { - switch runtime.GOOS { - case "linux", "android": - fwmarkIoctl = 36 /* unix.SO_MARK */ - case "freebsd": - fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */ - case "openbsd": - fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */ - } -} - -func SoMarkControl(c syscall.RawConn) error { - return c.Control(func(fd uintptr) { - //TODO: force to set 0xff. any chances to customize this value? - err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, fwmarkIoctl, 0x100) - if err != nil { - return - } - }) -} diff --git a/component/outbound/dialer/sockopt.go b/component/outbound/dialer/sockopt.go new file mode 100644 index 0000000..dec5dd3 --- /dev/null +++ b/component/outbound/dialer/sockopt.go @@ -0,0 +1,119 @@ +/* + * SPDX-License-Identifier: AGPL-3.0-only + * Copyright (c) since 2023, mzz2017 + */ + +package dialer + +import ( + "fmt" + "golang.org/x/sys/unix" + "net/netip" + "runtime" + "syscall" +) + +var fwmarkIoctl int + +func init() { + switch runtime.GOOS { + case "linux", "android": + fwmarkIoctl = 36 /* unix.SO_MARK */ + case "freebsd": + fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */ + case "openbsd": + fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */ + } +} + +func SoMarkControl(c syscall.RawConn, mark int) error { + var sockOptErr error + controlErr := c.Control(func(fd uintptr) { + err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, fwmarkIoctl, mark) + if err != nil { + sockOptErr = fmt.Errorf("error setting SO_MARK socket option: %w", err) + } + }) + if controlErr != nil { + return fmt.Errorf("error invoking socket control function: %w", controlErr) + } + return sockOptErr +} + +func TproxyControl(c syscall.RawConn) error { + var sockOptErr error + controlErr := c.Control(func(fd uintptr) { + // - https://www.kernel.org/doc/Documentation/networking/tproxy.txt + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TRANSPARENT, 1); err != nil { + sockOptErr = fmt.Errorf("error setting IP_TRANSPARENT socket option: %w", err) + return + } + + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { + sockOptErr = fmt.Errorf("error setting SO_REUSEADDR socket option: %w", err) + return + } + + e4 := unix.SetsockoptInt(int(fd), syscall.SOL_IP, unix.IP_RECVORIGDSTADDR, 1) + e6 := unix.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1) + if e4 != nil && e6 != nil { + if e4 != nil { + sockOptErr = fmt.Errorf("error setting IP_RECVORIGDSTADDR socket option: %w", e4) + } else { + sockOptErr = fmt.Errorf("error setting IPV6_RECVORIGDSTADDR socket option: %w", e6) + } + return + } + }) + if controlErr != nil { + return fmt.Errorf("error invoking socket control function: %w", controlErr) + } + return sockOptErr +} + +func BindControl(c syscall.RawConn, lAddrPort netip.AddrPort) error { + var sockOptErr error + controlErr := c.Control(func(fd uintptr) { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { + sockOptErr = fmt.Errorf("error setting IP_TRANSPARENT socket option: %w", err) + } + if err := bindAddr(fd, lAddrPort); err != nil { + sockOptErr = fmt.Errorf("error bindAddr: %w", err) + } + }) + if controlErr != nil { + return fmt.Errorf("error invoking socket control function: %w", controlErr) + } + return sockOptErr +} + +func bindAddr(fd uintptr, addrPort netip.AddrPort) error { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + return fmt.Errorf("error setting SO_REUSEADDR socket option: %w", err) + } + + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { + return fmt.Errorf("error setting SO_REUSEPORT socket option: %w", err) + } + + var sockAddr syscall.Sockaddr + + switch addr := addrPort.Addr().AsSlice(); len(addr) { + case 4: + a4 := &syscall.SockaddrInet4{ + Port: int(addrPort.Port()), + } + copy(a4.Addr[:], addr) + sockAddr = a4 + case 16: + a6 := &syscall.SockaddrInet6{ + Port: int(addrPort.Port()), + } + copy(a6.Addr[:], addr) + sockAddr = a6 + default: + return fmt.Errorf("unexpected length of ip") + } + + return syscall.Bind(int(fd), sockAddr) +} diff --git a/insert.sh b/insert.sh index 5a8fda9..d22d24a 100755 --- a/insert.sh +++ b/insert.sh @@ -3,7 +3,7 @@ lan=docker0 wan=wlp5s0 sudo tc qdisc add dev $lan clsact > /dev/null 2>&1 -sudo tc qdisc add dev $wan clsact > /dev/null 2>&1 +# sudo tc qdisc add dev $wan clsact > /dev/null 2>&1 set -ex @@ -16,8 +16,8 @@ sudo tc filter del dev $lan egress sudo tc filter del dev $wan ingress sudo tc filter del dev $wan egress sudo tc filter add dev $lan ingress bpf direct-action obj foo.o sec tc/ingress -sudo tc filter add dev $lan egress bpf direct-action obj foo.o sec tc/egress -sudo tc filter add dev $wan ingress bpf direct-action obj foo.o sec tc/wan_ingress -sudo tc filter add dev $wan egress bpf direct-action obj foo.o sec tc/wan_egress +# sudo tc filter add dev $lan egress bpf direct-action obj foo.o sec tc/egress +# sudo tc filter add dev $wan ingress bpf direct-action obj foo.o sec tc/wan_ingress +# sudo tc filter add dev $wan egress bpf direct-action obj foo.o sec tc/wan_egress exit 0 \ No newline at end of file