Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set IP_PKTINFO, one socket in UDP. #3

Open
zzxun opened this issue Oct 26, 2018 · 1 comment
Open

set IP_PKTINFO, one socket in UDP. #3

zzxun opened this issue Oct 26, 2018 · 1 comment

Comments

@zzxun
Copy link

zzxun commented Oct 26, 2018

When set IP_PKTINFO, dst ip will in oob as struct in_pktinfo.
By using this, it would only need one listening socket.

copy oob from ReadMsgUDP to WriteMsgUDP in golang

func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error)
func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error)

as in C

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

IP_PKTINFO (since Linux 2.2)
Pass an IP_PKTINFO ancillary message that contains a pktinfo structure that supplies some information about the incoming packet. This only works for datagram oriented sockets. The argument is a flag that tells the socket whether the IP_PKTINFO message should be passed or not. The message itself can only be sent/retrieved as control message with a packet using recvmsg(2) or sendmsg(2).

struct in_pktinfo {
    unsigned int   ipi_ifindex;  /* Interface index */
    struct in_addr ipi_spec_dst; /* Local address */
    struct in_addr ipi_addr;     /* Header Destination address */
};

ipi_ifindex is the unique index of the interface the packet was received on. ipi_spec_dst is the local address of the packet and ipi_addr is the destination address in the packet header. If IP_PKTINFO is passed to sendmsg(2) and ipi_spec_dst is not zero, then it is used as the local source address for the routing table lookup and for setting up IP source route options. When ipi_ifindex is not zero, the primary local address of the interface specified by the index overwrites ipi_spec_dst for the routing table lookup.

In golang, in_pktinfo is syscall.Inet4Pktinfo

type Inet4Pktinfo struct {
	Ifindex  uint32
	Spec_dst [4]byte /* in_addr */
	Addr     [4]byte /* in_addr */
}

Modify demo:

// tproxy_udp.go
func ListenUDP(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
...
	if err = syscall.SetsockoptInt(fileDescriptor, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
		syscall.Close(fileDescriptor)
		return nil, &net.OpError{Op: "listen", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %s", err)}
	}

	if err = syscall.SetsockoptInt(fileDescriptor, syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
		syscall.Close(fileDescriptor)
		return nil, &net.OpError{Op: "listen", Err: fmt.Errorf("set socket option: IP_PKTINFO: %s", err)}
	}
...
}

func ReadFromUDP(conn *net.UDPConn, b []byte) (int, []byte, *net.UDPAddr, *net.UDPAddr, error) {
...
	for _, msg := range msgs {
		if msg.Header.Level == syscall.IPPROTO_IP && msg.Header.Type == syscall.IP_PKTINFO {
			originalDstRaw := &syscall.Inet4Pktinfo{}
			if err = binary.Read(bytes.NewReader(msg.Data), binary.LittleEndian, originalDstRaw); err != nil {
				return 0, nil, nil, nil, fmt.Errorf("reading original destination address: %s", err)
			}

			// switch originalDstRaw.Family {
			// case syscall.AF_INET:
			pp := (*syscall.Inet4Pktinfo)(unsafe.Pointer(originalDstRaw))
			// p := (*[2]byte)(unsafe.Pointer(&pp.Port))
			originalDst = &net.UDPAddr{
				IP:   net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3]),
				Port: 53,
			}
...
// example/tproxy_example.go
func handleUDPConn(data []byte, oob []byte, srcAddr, dstAddr *net.UDPAddr) {
...
  // oob copy from ReadMsgUDP
  bytesWritten, _, err = udpListener.WriteMsgUDP(data, oob, srcAddr)
...
@FH0
Copy link

FH0 commented Dec 25, 2020

IP_PKTINFO can't get port.
IP_ORIGDSTADDR can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants