From 555fea98b70bb0671dd95fd1cb7fdf714fc97319 Mon Sep 17 00:00:00 2001 From: ddddddO Date: Sun, 9 Jun 2024 19:24:45 +0900 Subject: [PATCH] drop rst packet --- Makefile | 4 + cmd/packemon/egress_packet.bpf.c | 177 ++++++++++++++++++++++++ cmd/packemon/egress_packet_bpfeb.go | 122 ++++++++++++++++ cmd/packemon/egress_packet_bpfeb.o | Bin 0 -> 10536 bytes cmd/packemon/egress_packet_bpfel.go | 122 ++++++++++++++++ cmd/packemon/egress_packet_bpfel.o | Bin 0 -> 10592 bytes cmd/packemon/gen.go | 3 + cmd/packemon/main.go | 125 ++++++++++++++--- go.mod | 4 + go.sum | 9 ++ http.go | 2 +- internal/tui/form.go | 5 +- internal/tui/form_http.go | 57 ++++++-- internal/tui/tui.go | 5 +- networkinterface.go | 43 +++--- tcp.go | 207 +++++++++++++++++++++++++++- 16 files changed, 829 insertions(+), 56 deletions(-) create mode 100644 cmd/packemon/egress_packet.bpf.c create mode 100644 cmd/packemon/egress_packet_bpfeb.go create mode 100644 cmd/packemon/egress_packet_bpfeb.o create mode 100644 cmd/packemon/egress_packet_bpfel.go create mode 100644 cmd/packemon/egress_packet_bpfel.o create mode 100644 cmd/packemon/gen.go diff --git a/Makefile b/Makefile index 2117b84..3e17f6e 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +# cmd/packemon/ 配下で以下 +# go generate +# sudo go run . + # いかな感じで単発で送信 debug: sudo go run cmd/packemon/main.go --send --proto icmp --debug diff --git a/cmd/packemon/egress_packet.bpf.c b/cmd/packemon/egress_packet.bpf.c new file mode 100644 index 0000000..6896f82 --- /dev/null +++ b/cmd/packemon/egress_packet.bpf.c @@ -0,0 +1,177 @@ +//go:build ignore + +// 部分的にコピーしてる +// https://eunomia.dev/tutorials/20-tc/#writing-ebpf-programs + +// 以下あたりの定義も拝借. ethhdr/iphdr など +// https://github.com/cilium/ebpf/blob/b8dc0ee25417ce7cd4a6feb48be42c0615ee9043/examples/headers/common.h#L4 + +// #include +// #include "common.h" +#include +#include +#include +#include + +#define TC_ACT_OK 0 +#define TC_ACT_SHOT 2 + +#define ETH_P_IPv4 0x0800 +#define ETH_P_ARP 0x0806 + +#define IP_P_ICMP 0x01 +#define IP_P_TCP 0x06 +#define IP_P_UDP 0x17 + +// Wireshark観察する限りはそうだが要fix +#define TCP_FLG_RST_ACK 0x29 +// bpfのlog観察する限りはそうだが要fix +#define TCP_FLG_RST 0x8 + +#define MAX_ENTRIES 64 +#define AF_INET 2 + +struct ethhdr { + unsigned char h_dest[6]; + unsigned char h_source[6]; + __be16 h_proto; +}; + +struct iphdr { + __u8 ihl: 4; + __u8 version: 4; + __u8 tos; + __be16 tot_len; + __be16 id; + __be16 frag_off; + __u8 ttl; + __u8 protocol; + __sum16 check; + __be32 saddr; + __be32 daddr; +}; + +// https://www.infraexpert.com/study/tcpip8.html +struct tcphdr { + __be16 sport; + __be16 dport; + __be32 sequence; + __be32 acknowladge; + __u8 offset: 4; + __u8 yoyaku: 3; + __be16 controlflg: 9; + __be16 window; + __be16 checksum; + __be16 urg; +}; + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, 1); +} pkt_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, __u64); + __uint(max_entries, 1); +} arp_pkt_count SEC(".maps"); + +// __sk_buff について +// https://medium.com/@c0ngwang/understanding-struct-sk-buff-730cf847a722 + +SEC("tc") +int control_egress(struct __sk_buff *skb) +{ + void *data_end = (void *)(__u64)skb->data_end; + void *data = (void *)(__u64)skb->data; + struct ethhdr *eth; + struct iphdr *iph; + struct tcphdr *tcph; + + __u32 key = 0; + __u64 *count = bpf_map_lookup_elem(&pkt_count, &key); + __u64 *arp_count = bpf_map_lookup_elem(&arp_pkt_count, &key); + + // bpf_printk("proto: %x", skb->protocol); + // bpf_printk("data: %x", skb->data); + // bpf_printk("data_end: %x", skb->data_end); + + if (count) { + __sync_fetch_and_add(count, 1); + } + + eth = data; + if ((void *)(eth + 1) > data_end) { + bpf_printk("a"); + return TC_ACT_OK; + } + + iph = (struct iphdr *)(eth + 1); + if ((void *)(iph + 1) > data_end) { + bpf_printk("b"); + return TC_ACT_OK; + } + + if (bpf_ntohs(eth->h_proto) == ETH_P_ARP) { + bpf_printk("Ether Type: ARP"); + if (arp_count) { + __sync_fetch_and_add(arp_count, 1); + } + // return TC_ACT_SHOT; + return TC_ACT_OK; + } + + if (bpf_ntohs(eth->h_proto) == ETH_P_IPv4) { + bpf_printk("Ether Type : IP"); + bpf_printk(" tot_len : %d", bpf_ntohs(iph->tot_len)); + bpf_printk(" ttl : %d", iph->ttl); + bpf_printk(" protocol: %x", iph->protocol); + bpf_printk(" dst : %x", bpf_ntohl(iph->daddr)); + bpf_printk(" src : %x", bpf_ntohl(iph->saddr)); + + if (iph->protocol == IP_P_ICMP) { + bpf_printk("ICMP"); + return TC_ACT_OK; + } + if (iph->protocol == IP_P_UDP) { + bpf_printk("UDP"); + return TC_ACT_OK; + } + if (iph->protocol == IP_P_TCP) { + bpf_printk("TCP"); + + tcph = (struct tcphdr *)(iph + 1); + if ((void *)(tcph + 1) > data_end) { + bpf_printk("c"); + return TC_ACT_OK; + } + + bpf_printk(" sport : %x", bpf_ntohs(tcph->sport)); + bpf_printk(" dport : %x", bpf_ntohs(tcph->dport)); + bpf_printk(" controlflg: %x", bpf_ntohs(tcph->controlflg)); + bpf_printk(" controlflg: %x", tcph->controlflg); + + if (tcph->controlflg == TCP_FLG_RST_ACK) { + bpf_printk("TCP RST-ACK"); + return TC_ACT_SHOT; + // return TC_ACT_OK; + } + if (tcph->controlflg == TCP_FLG_RST) { + bpf_printk("TCP RST"); + return TC_ACT_SHOT; + // return TC_ACT_OK; + } + + return TC_ACT_OK; + } + + return TC_ACT_OK; + } + + return TC_ACT_OK; +} \ No newline at end of file diff --git a/cmd/packemon/egress_packet_bpfeb.go b/cmd/packemon/egress_packet_bpfeb.go new file mode 100644 index 0000000..3e4b138 --- /dev/null +++ b/cmd/packemon/egress_packet_bpfeb.go @@ -0,0 +1,122 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build mips || mips64 || ppc64 || s390x + +package main + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +// loadEgress_packet returns the embedded CollectionSpec for egress_packet. +func loadEgress_packet() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_Egress_packetBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load egress_packet: %w", err) + } + + return spec, err +} + +// loadEgress_packetObjects loads egress_packet and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *egress_packetObjects +// *egress_packetPrograms +// *egress_packetMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadEgress_packetObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadEgress_packet() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// egress_packetSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetSpecs struct { + egress_packetProgramSpecs + egress_packetMapSpecs +} + +// egress_packetSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetProgramSpecs struct { + ControlEgress *ebpf.ProgramSpec `ebpf:"control_egress"` +} + +// egress_packetMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetMapSpecs struct { + ArpPktCount *ebpf.MapSpec `ebpf:"arp_pkt_count"` + PktCount *ebpf.MapSpec `ebpf:"pkt_count"` +} + +// egress_packetObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetObjects struct { + egress_packetPrograms + egress_packetMaps +} + +func (o *egress_packetObjects) Close() error { + return _Egress_packetClose( + &o.egress_packetPrograms, + &o.egress_packetMaps, + ) +} + +// egress_packetMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetMaps struct { + ArpPktCount *ebpf.Map `ebpf:"arp_pkt_count"` + PktCount *ebpf.Map `ebpf:"pkt_count"` +} + +func (m *egress_packetMaps) Close() error { + return _Egress_packetClose( + m.ArpPktCount, + m.PktCount, + ) +} + +// egress_packetPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetPrograms struct { + ControlEgress *ebpf.Program `ebpf:"control_egress"` +} + +func (p *egress_packetPrograms) Close() error { + return _Egress_packetClose( + p.ControlEgress, + ) +} + +func _Egress_packetClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed egress_packet_bpfeb.o +var _Egress_packetBytes []byte diff --git a/cmd/packemon/egress_packet_bpfeb.o b/cmd/packemon/egress_packet_bpfeb.o new file mode 100644 index 0000000000000000000000000000000000000000..dec3f9ecb53608c257751029121b7d56943a129c GIT binary patch literal 10536 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 GZT|)!I?#&% literal 0 HcmV?d00001 diff --git a/cmd/packemon/egress_packet_bpfel.go b/cmd/packemon/egress_packet_bpfel.go new file mode 100644 index 0000000..0b9daae --- /dev/null +++ b/cmd/packemon/egress_packet_bpfel.go @@ -0,0 +1,122 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 + +package main + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +// loadEgress_packet returns the embedded CollectionSpec for egress_packet. +func loadEgress_packet() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_Egress_packetBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load egress_packet: %w", err) + } + + return spec, err +} + +// loadEgress_packetObjects loads egress_packet and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *egress_packetObjects +// *egress_packetPrograms +// *egress_packetMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadEgress_packetObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadEgress_packet() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// egress_packetSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetSpecs struct { + egress_packetProgramSpecs + egress_packetMapSpecs +} + +// egress_packetSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetProgramSpecs struct { + ControlEgress *ebpf.ProgramSpec `ebpf:"control_egress"` +} + +// egress_packetMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type egress_packetMapSpecs struct { + ArpPktCount *ebpf.MapSpec `ebpf:"arp_pkt_count"` + PktCount *ebpf.MapSpec `ebpf:"pkt_count"` +} + +// egress_packetObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetObjects struct { + egress_packetPrograms + egress_packetMaps +} + +func (o *egress_packetObjects) Close() error { + return _Egress_packetClose( + &o.egress_packetPrograms, + &o.egress_packetMaps, + ) +} + +// egress_packetMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetMaps struct { + ArpPktCount *ebpf.Map `ebpf:"arp_pkt_count"` + PktCount *ebpf.Map `ebpf:"pkt_count"` +} + +func (m *egress_packetMaps) Close() error { + return _Egress_packetClose( + m.ArpPktCount, + m.PktCount, + ) +} + +// egress_packetPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadEgress_packetObjects or ebpf.CollectionSpec.LoadAndAssign. +type egress_packetPrograms struct { + ControlEgress *ebpf.Program `ebpf:"control_egress"` +} + +func (p *egress_packetPrograms) Close() error { + return _Egress_packetClose( + p.ControlEgress, + ) +} + +func _Egress_packetClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed egress_packet_bpfel.o +var _Egress_packetBytes []byte diff --git a/cmd/packemon/egress_packet_bpfel.o b/cmd/packemon/egress_packet_bpfel.o new file mode 100644 index 0000000000000000000000000000000000000000..2a8d33e5459ff77ce32371b47223224f16c7ff71 GIT binary patch literal 10592 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 H0MdT}^f%8i literal 0 HcmV?d00001 diff --git a/cmd/packemon/gen.go b/cmd/packemon/gen.go new file mode 100644 index 0000000..45f1a6a --- /dev/null +++ b/cmd/packemon/gen.go @@ -0,0 +1,3 @@ +package main + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go egress_packet egress_packet.bpf.c diff --git a/cmd/packemon/main.go b/cmd/packemon/main.go index 4bf74fe..758a01a 100644 --- a/cmd/packemon/main.go +++ b/cmd/packemon/main.go @@ -4,12 +4,20 @@ import ( "errors" "flag" "fmt" + "log" + "net" "os" + "os/signal" "strings" + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/rlimit" "github.com/ddddddO/packemon" "github.com/ddddddO/packemon/internal/debugging" "github.com/ddddddO/packemon/internal/tui" + "github.com/vishvananda/netlink" + + "golang.org/x/sys/unix" ) const DEFAULT_TARGET_NW_INTERFACE = "eth0" @@ -25,13 +33,94 @@ func main() { flag.StringVar(&protocol, "proto", "", "Specify either 'arp', 'icmp', 'tcp', 'dns' or 'http'.") flag.Parse() - if err := run(nwInterface, wantSend, debug, protocol); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) + if wantSend { + // Generator で3way handshake する際に、カーネルが自動でRSTパケットを送ってたため、ドロップするため + ebpfProg, qdisc, err := prepareDropingRSTPacket(nwInterface) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer func() { + ebpfProg.Close() + // 以下で消しておかないと、再起動やtcコマンド使わない限り、RSTパケットがカーネルから送信されない状態になる + if err := netlink.QdiscDel(qdisc); err != nil { + log.Printf("Failed to QdiscDel. Please PC reboot... Error: %s\n", err) + } + }() + } + + stop := make(chan os.Signal, 5) + signal.Notify(stop, os.Interrupt) + // 以下でもctl-cしないといけない + go run(stop, nwInterface, wantSend, debug, protocol) + <-stop + log.Print("Received signal, exiting...") +} + +func prepareDropingRSTPacket(nwInterface string) (*egress_packetObjects, *netlink.GenericQdisc, error) { + // Remove resource limits for kernels <5.11. + if err := rlimit.RemoveMemlock(); err != nil { + return nil, nil, fmt.Errorf("removing memlock: %w", err) + } + + // Load the compiled eBPF ELF and load it into the kernel. + var objs egress_packetObjects + if err := loadEgress_packetObjects(&objs, nil); err != nil { + return nil, nil, fmt.Errorf("loading eBPF objects: %w", err) + } + + qdisc, err := attachFilter(nwInterface, objs.egress_packetPrograms.ControlEgress) + if err != nil { + return nil, nil, fmt.Errorf("failed to attach: %w", err) } + + return &objs, qdisc, nil } -func run(nwInterface string, wantSend bool, debug bool, protocol string) error { +// https://github.com/fedepaol/tc-return/blob/main/main.go +func attachFilter(attachTo string, program *ebpf.Program) (*netlink.GenericQdisc, error) { + devID, err := net.InterfaceByName(attachTo) + if err != nil { + return nil, fmt.Errorf("could not get interface ID: %w", err) + } + + qdisc := &netlink.GenericQdisc{ + QdiscAttrs: netlink.QdiscAttrs{ + LinkIndex: devID.Index, + Handle: netlink.MakeHandle(0xffff, 0), + Parent: netlink.HANDLE_CLSACT, + }, + QdiscType: "clsact", + } + + err = netlink.QdiscReplace(qdisc) + if err != nil { + return nil, fmt.Errorf("could not get replace qdisc: %w", err) + } + + filter := &netlink.BpfFilter{ + FilterAttrs: netlink.FilterAttrs{ + LinkIndex: devID.Index, + Parent: netlink.HANDLE_MIN_EGRESS, + Handle: 1, + Protocol: unix.ETH_P_ALL, + }, + Fd: program.FD(), + Name: program.String(), + DirectAction: true, + } + + if err := netlink.FilterReplace(filter); err != nil { + return nil, fmt.Errorf("failed to replace tc filter: %w", err) + } + + return qdisc, nil +} + +func run(stop <-chan os.Signal, nwInterface string, wantSend bool, debug bool, protocol string) error { + // packemonを終了した後でsignal飛ばしてもらって終了させてもらう、一旦 + fmt.Println("Terminate packemon with ctl-c.") + netIf, err := packemon.NewNetworkInterface(nwInterface) if err != nil { return err @@ -41,8 +130,6 @@ func run(nwInterface string, wantSend bool, debug bool, protocol string) error { tui.DEFAULT_MAC_SOURCE = fmt.Sprintf("0x%s", strings.ReplaceAll(netIf.Intf.HardwareAddr.String(), ":", "")) tui.DEFAULT_ARP_SENDER_MAC = tui.DEFAULT_MAC_SOURCE - fmt.Printf("Monitor interface: %v\n", netIf.Intf) - ipAddr, err := netIf.Intf.Addrs() if err != nil { return err @@ -68,31 +155,33 @@ func run(nwInterface string, wantSend bool, debug bool, protocol string) error { } // Monitor の debug は本チャンの networkinterface.go 使うようにする - go netIf.Recieve() - return debugPrint(netIf.PassiveCh) + go netIf.Recieve(stop) + return debugPrint(stop, netIf.PassiveCh) } if wantSend { tui.DEFAULT_NW_INTERFACE = nwInterface tui := tui.NewTUI(wantSend) - return tui.Generator(netIf.Send) + return tui.Generator(stop, netIf.Send) } else { tui := tui.NewTUI(wantSend) - go netIf.Recieve() + go netIf.Recieve(stop) return tui.Monitor(netIf.PassiveCh) } } -func debugPrint(passive <-chan *packemon.Passive) error { - for p := range passive { - if p.HighLayerProto() == "IPv6" { - fmt.Println("Passive!") - fmt.Printf("%x\n", p.IPv6) +func debugPrint(stop <-chan os.Signal, passive <-chan *packemon.Passive) error { + for { + select { + case <-stop: + return nil + case p := <-passive: + if p.HighLayerProto() == "IPv6" { + fmt.Println("Passive!") + fmt.Printf("%x\n", p.IPv6) + } } - } - - return nil } func debugMode(wantSend bool, protocol string, netIf *packemon.NetworkInterface, dstMacAddr [6]byte) error { diff --git a/go.mod b/go.mod index 0878239..48e51d6 100644 --- a/go.mod +++ b/go.mod @@ -9,10 +9,14 @@ require ( ) require ( + github.com/cilium/ebpf v0.15.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/vishvananda/netlink v1.1.0 // indirect + github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index c0c2dc2..7b3f752 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= +github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= @@ -12,9 +14,15 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -25,6 +33,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/http.go b/http.go index 7a554f5..42aa91e 100644 --- a/http.go +++ b/http.go @@ -126,7 +126,7 @@ func ParsedHTTPResponse(payload []byte) *HTTPResponse { continue } - log.Printf("not suported header: %s, len: %d\n", string(s), len(bytes.TrimSpace(s))) + // log.Printf("not suported header: %s, len: %d\n", string(s), len(bytes.TrimSpace(s))) } b := bytes.SplitAfter(payload, append(sep, sep...)) body := string(b[len(b)-1][0:header.ContentLength]) diff --git a/internal/tui/form.go b/internal/tui/form.go index 04d36c4..5b0d36d 100644 --- a/internal/tui/form.go +++ b/internal/tui/form.go @@ -2,6 +2,7 @@ package tui import ( "encoding/binary" + "os" "strconv" "strings" @@ -68,14 +69,14 @@ var ( // 長さとか他のフィールドに基づいて計算しないといけない値があるから、そこは固定値ではなくてリアルタイムに反映したい // とすると、高レイヤーの入力から埋めて進めていくようにしないといけなさそう. ユーザーが選べるようにするのがいいかも -func (t *tui) form(sendFn func(*packemon.EthernetFrame) error) error { +func (t *tui) form(stop <-chan os.Signal, sendFn func(*packemon.EthernetFrame) error) error { d, err := defaultPackets() if err != nil { return err } ethernetHeader, arp, ipv4, icmp, udp, tcp, dns, http := d.e, d.a, d.ip, d.ic, d.u, d.t, d.d, d.h - httpForm := t.httpForm(sendFn, ethernetHeader, ipv4, tcp, http) + httpForm := t.httpForm(stop, sendFn, ethernetHeader, ipv4, tcp, http) httpForm.SetBorder(true).SetTitle(" HTTP ").SetTitleAlign(tview.AlignLeft) dnsForm := t.dnsForm(sendFn, ethernetHeader, ipv4, udp, dns) dnsForm.SetBorder(true).SetTitle(" DNS ").SetTitleAlign(tview.AlignLeft) diff --git a/internal/tui/form_http.go b/internal/tui/form_http.go index f60e310..c158ecd 100644 --- a/internal/tui/form_http.go +++ b/internal/tui/form_http.go @@ -2,12 +2,31 @@ package tui import ( "encoding/binary" + "os" "github.com/ddddddO/packemon" "github.com/rivo/tview" ) -func (t *tui) httpForm(sendFn func(*packemon.EthernetFrame) error, ethernetHeader *packemon.EthernetHeader, ipv4 *packemon.IPv4, tcp *packemon.TCP, http *packemon.HTTP) *tview.Form { +// var threeWayHandshakeDescription = "When 3-way handshake for TCP is enabled, only \n\n - HTTP section\n - TCP Destination Port\n - IPv4 Destination IP Addr\n\nare reflected as input." + +var threeWayHandshakeDescription = "" + + "When 3-way handshake for TCP is enabled, only" + "\n\n" + + " HTTP" + "\n" + + " - All" + "\n" + + " TCP" + "\n" + + " - Source Port" + "\n" + + " - Destination Port" + "\n" + + " IPv4" + "\n" + + " - Source IP Addr" + "\n" + + " - Destination IP Addr" + "\n" + + " Ethernet" + "\n" + + " - Destination MAC Addr" + "\n" + + " - Source MAC Addr" + "\n" + + "\n" + + "are reflected as input." + +func (t *tui) httpForm(stop <-chan os.Signal, sendFn func(*packemon.EthernetFrame) error, ethernetHeader *packemon.EthernetHeader, ipv4 *packemon.IPv4, tcp *packemon.TCP, http *packemon.HTTP) *tview.Form { do3wayHandshake := false httpForm := tview.NewForm(). @@ -15,7 +34,7 @@ func (t *tui) httpForm(sendFn func(*packemon.EthernetFrame) error, ethernetHeade AddCheckbox("Do 3way handshake ?", do3wayHandshake, func(checked bool) { do3wayHandshake = checked }). - AddTextView("", "When 3-way handshake for TCP is enabled, only \n\n - HTTP section\n - TCP Destination Port\n - IPv4 Destination IP Addr\n\nare reflected as input.", 60, 7, true, true). + AddTextView("", threeWayHandshakeDescription, 60, 15, true, true). AddInputField("Method", DEFAULT_HTTP_METHOD, 10, func(textToCheck string, lastChar rune) bool { if len(textToCheck) <= 10 { http.Method = textToCheck @@ -65,16 +84,30 @@ func (t *tui) httpForm(sendFn func(*packemon.EthernetFrame) error, ethernetHeade if do3wayHandshake { dstIPAddr := make([]byte, 4) binary.BigEndian.PutUint32(dstIPAddr, ipv4.DstAddr) - if err := packemon.EstablishConnectionAndSendPayload( - DEFAULT_NW_INTERFACE, - dstIPAddr, - tcp.DstPort, - // []byte{0xc0, 0xa8, 0x0a, 0x6e}, - // 0x0050, - http.Bytes(), - ); err != nil { - t.addErrPage(err) - } + // if err := packemon.EstablishConnectionAndSendPayload( + // DEFAULT_NW_INTERFACE, + // dstIPAddr, + // tcp.DstPort, + // // []byte{0xc0, 0xa8, 0x0a, 0x6e}, + // // 0x0050, + // http.Bytes(), + // ); err != nil { + // t.addErrPage(err) + // } + + go func() { + if err := packemon.EstablishConnectionAndSendPayloadXxx( + stop, + DEFAULT_NW_INTERFACE, + ethernetHeader, + ipv4, + tcp, + http, + ); err != nil { + t.addErrPage(err) + } + }() + return } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index c904c2f..49837da 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -1,6 +1,7 @@ package tui import ( + "os" "sync" "github.com/ddddddO/packemon" @@ -57,8 +58,8 @@ func newMonitor() *tui { } } -func (t *tui) Generator(sendFn func(*packemon.EthernetFrame) error) error { - if err := t.form(sendFn); err != nil { +func (t *tui) Generator(stop <-chan os.Signal, sendFn func(*packemon.EthernetFrame) error) error { + if err := t.form(stop, sendFn); err != nil { return err } return t.app.SetRoot(t.grid, true).EnableMouse(true).SetFocus(t.grid).Run() diff --git a/networkinterface.go b/networkinterface.go index 1cdf953..ee7ecea 100644 --- a/networkinterface.go +++ b/networkinterface.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "net" + "os" "strings" "golang.org/x/sys/unix" @@ -88,7 +89,7 @@ func (nw *NetworkInterface) Send(ethernetFrame *EthernetFrame) error { return unix.Sendto(nw.Socket, ethernetFrame.Bytes(), 0, &nw.SocketAddr) } -func (nw *NetworkInterface) Recieve() error { +func (nw *NetworkInterface) Recieve(stop <-chan os.Signal) error { epollfd, err := unix.EpollCreate1(0) if err != nil { return err @@ -108,28 +109,36 @@ func (nw *NetworkInterface) Recieve() error { events := make([]unix.EpollEvent, 10) for { - fds, err := unix.EpollWait(epollfd, events, -1) - if err != nil { - return err - } + select { + case <-stop: + return nil + default: + fds, err := unix.EpollWait(epollfd, events, -1) + if err != nil { + return err + } - for i := 0; i < fds; i++ { - if events[i].Fd == int32(nw.Socket) { - recieved := make([]byte, 1500) - n, _, err := unix.Recvfrom(nw.Socket, recieved, 0) - if err != nil { - if n == -1 { - continue + for i := 0; i < fds; i++ { + select { + case <-stop: + return nil + default: + if events[i].Fd == int32(nw.Socket) { + recieved := make([]byte, 1500) + n, _, err := unix.Recvfrom(nw.Socket, recieved, 0) + if err != nil { + if n == -1 { + continue + } + return err + } + + nw.PassiveCh <- ParsedPacket(recieved) } - return err } - - nw.PassiveCh <- ParsedPacket(recieved) } } } - - return nil } func (nw *NetworkInterface) Close() error { diff --git a/tcp.go b/tcp.go index 4cf2849..5b049fd 100644 --- a/tcp.go +++ b/tcp.go @@ -3,6 +3,9 @@ package packemon import ( "bytes" "encoding/binary" + "os" + + "golang.org/x/sys/unix" ) const ( @@ -171,8 +174,204 @@ func EstablishConnectionAndSendPayload(nwInterface string, dstIPAddr []byte, dst return nil } -// TODO: debugNetworkInterface の SendTCP3wayhandshake をベースに。上の関数はOSが良しなに3way handshakeしてくれるやつ -// L7用のコールバックを渡すようにするとイイかも。引数にtcpのシーケンスと確認応答の番号、返り値にEthernetFrameな感じで。引数はもう少し必要なものあるかな -func EstablishConnectionAndSendPayloadXxx() error { - return nil +// このなかで、ログ出力などしないこと。Monitor の下に出てくる +// 挙動を詳細に確認する場合は、internal内の SendTCP3wayhandshake 関数でやること +// TODO: 対向からRST,RST/ACKが来た時にreturnするようにする +// TODO: http専用になっちゃってるから、他のプロトコルでも使えるよう汎用的にする +func EstablishConnectionAndSendPayloadXxx(stop <-chan os.Signal, nwInterface string, fEthrh *EthernetHeader, fIpv4 *IPv4, fTcp *TCP, fHttp *HTTP) error { + nw, err := NewNetworkInterface(nwInterface) + if err != nil { + return err + } + + var srcPort uint16 = fTcp.SrcPort + var dstPort uint16 = fTcp.DstPort + var srcIPAddr uint32 = fIpv4.SrcAddr + var dstIPAddr uint32 = fIpv4.DstAddr + dstMACAddr := fEthrh.Dst + srcMACAddr := fEthrh.Src + + tcp := NewTCPSyn(srcPort, dstPort) + ipv4 := NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + + epollfd, err := unix.EpollCreate1(0) + if err != nil { + return err + } + + if err := unix.EpollCtl( + epollfd, + unix.EPOLL_CTL_ADD, + nw.Socket, + &unix.EpollEvent{ + Events: unix.EPOLLIN, + Fd: int32(nw.Socket), + }, + ); err != nil { + return err + } + + events := make([]unix.EpollEvent, 10) + for { + select { + case <-stop: + return nil + + default: + fds, err := unix.EpollWait(epollfd, events, -1) + if err != nil { + return err + } + + for i := 0; i < fds; i++ { + select { + case <-stop: + return nil + default: + if events[i].Fd == int32(nw.Socket) { + recieved := make([]byte, 1500) + n, _, err := unix.Recvfrom(nw.Socket, recieved, 0) + if err != nil { + if n == -1 { + continue + } + return err + } + + ethernetFrame := ParsedEthernetFrame(recieved) + + switch ethernetFrame.Header.Typ { + case ETHER_TYPE_IPv4: + ipv4 := ParsedIPv4(ethernetFrame.Data) + + switch ipv4.Protocol { + case IPv4_PROTO_TCP: + tcp := ParsedTCP(ipv4.Data) + + switch tcp.DstPort { + case srcPort: // synパケットの送信元ポート + if tcp.Flags == TCP_FLAGS_SYN_ACK { + // log.Println("passive TCP_FLAGS_SYN_ACK") + + // syn/ackを受け取ったのでack送信 + tcp := NewTCPAck(srcPort, dstPort, tcp.Sequence, tcp.Acknowledgment) + ipv4 := NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + + tcp = NewTCPWithData(srcPort, dstPort, fHttp.Bytes(), tcp.Sequence, tcp.Acknowledgment) + ipv4 = NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + + continue + } + + if tcp.Flags == TCP_FLAGS_ACK { + // log.Println("passive TCP_FLAGS_ACK") + continue + } + + if tcp.Flags == TCP_FLAGS_PSH_ACK { + lineLength := bytes.Index(tcp.Data, []byte{0x0d, 0x0a}) // "\r\n" + if lineLength == -1 { + // log.Println("-1") + continue + } + // log.Println("passive TCP_FLAGS_PSH_ACK") + + // HTTPレスポンス受信 + if tcp.SrcPort == PORT_HTTP { + resp := ParsedHTTPResponse(tcp.Data) + // log.Printf("%+v\n", resp) + + // そのackを返す + // log.Printf("Length of http resp: %d\n", resp.Len()) + + tcp := NewTCPAckForPassiveData(srcPort, dstPort, tcp.Sequence, tcp.Acknowledgment, resp.Len()) + ipv4 := NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + + // 続けてFinAck + tcp = NewTCPFinAck(srcPort, dstPort, tcp.Sequence, tcp.Acknowledgment) + ipv4 = NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame = NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + } + continue + } + + if tcp.Flags == TCP_FLAGS_FIN_ACK { + // log.Println("passive TCP_FLAGS_FIN_ACK") + + // それにack + tcp := NewTCPAck(srcPort, dstPort, tcp.Sequence, tcp.Acknowledgment) + ipv4 := NewIPv4(IPv4_PROTO_TCP, srcIPAddr, dstIPAddr) + tcp.CalculateChecksum(ipv4) + + ipv4.Data = tcp.Bytes() + ipv4.CalculateTotalLength() + ipv4.CalculateChecksum() + + ethernetFrame := NewEthernetFrame(dstMACAddr, srcMACAddr, ETHER_TYPE_IPv4, ipv4.Bytes()) + if err := nw.Send(ethernetFrame); err != nil { + return err + } + return nil + } + + continue + default: + // noop + } + } + } + } + } + } + } + } }