То, что я пытаюсь сделать, это прослушать кадры ethe rnet для IPv6 и ответить на вызовы UDP на конкретном c порту.
Я могу захватить кадры ethe rnet I заботиться о полезной нагрузке UDP и разбирать ее, но когда я пытаюсь повторить эту полезную нагрузку, у меня возникает проблема. Вот мой "серверный" код:
func main() {
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IPV6)))
iface, err := net.InterfaceByName("lo")
if err != nil {
log.Fatal(err)
}
err = syscall.BindToDevice(fd, iface.Name)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, iface.MTU)
n, callerAddr, err := syscall.Recvfrom(fd, buf, 0)
if err != nil {
log.Fatal(err)
}
data := buf[:n]
packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
udpPacket := packet.Layer(layers.LayerTypeUDP)
if udpPacket != nil {
udpPck, _ := udpPacket.(*layers.UDP)
// I only care about calls to 8080 for this example
if udpPck.DstPort != 8080 {
continue
}
err = udpPck.SetNetworkLayerForChecksum(packet.NetworkLayer()); if err != nil {
log.Fatal(err)
}
log.Print(packet)
log.Printf("UDP Port from %v --> %v", udpPck.SrcPort, udpPck.DstPort)
log.Printf("Payload '%v'", string(udpPck.Payload))
// Flip the source and destination so it can go back to the caller
ogDst := udpPck.DstPort
udpPck.DstPort = udpPck.SrcPort
udpPck.SrcPort = ogDst
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{ComputeChecksums: true}
// Rebuild the packet with the new source and destination port
err := gopacket.SerializePacket(buffer, options, packet)
if err != nil {
log.Fatal(err)
}
log.Printf("Writing the payload back to the caller: %v", callerAddr)
log.Print(packet)
err = syscall.Sendto(fd, buffer.Bytes(), 0, callerAddr)
if err != nil {
log.Fatal(err)
}
}
}
А затем мой клиентский код, работающий на том же компьютере:
func main() {
conn, err := net.DialUDP("udp6", &net.UDPAddr{
IP: net.IPv6loopback,
Port: 0,
}, &net.UDPAddr{
IP: net.IPv6loopback,
Port: 8080,
})
if err != nil {
log.Fatal(err)
}
_, _ = conn.Write([]byte("Hello World"))
log.Print("Waiting for response")
buf := make([]byte, 65535)
n, _, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("Response message '%v'", string(buf[:n]))
}
Проблема со стороны клиента - отказ в соединении read udp6 [::1]:56346->[::1]:8080: recvfrom: connection refused
, который, как я предполагаю, будет исходить от ядра linux, поскольку я, строго говоря, не привязал ничего к 8080.
Мне нужны данные из заголовка IPv6 (не видно выше), поэтому мне нужны для прослушивания на уровне канала передачи данных, но поскольку мне также нужно отвечать на запросы UDP, все становится немного сложнее.
У меня есть вариант, но я не хочу, чтобы в отдельной горутине выполнялась стандартная net.ListenUDP
, а затем блокируется после чтения данных до тех пор, пока заголовок IPv6 не будет прочитан из прослушивателя сокета syscall, а затем оттуда отвечает на соединение udp. Если это мой единственный вариант, я воспользуюсь им, но мне было бы интересно посмотреть, могу ли я сделать что-нибудь получше.