diff --git a/router/forwarder.go b/router/forwarder.go index e9f0596d..94eed9f7 100644 --- a/router/forwarder.go +++ b/router/forwarder.go @@ -3,8 +3,6 @@ package router import ( "code.google.com/p/gopacket" "code.google.com/p/gopacket/layers" - "log" - "net" "syscall" "time" ) @@ -342,112 +340,3 @@ func (fwd *Forwarder) drain() { func (fwd *Forwarder) logDrop(frame *ForwardedFrame) { fwd.conn.log("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead()) } - -// UDP Senders - -func NewSimpleUDPSender(conn *LocalConnection) *SimpleUDPSender { - return &SimpleUDPSender{udpConn: conn.Router.UDPListener, conn: conn} -} - -func (sender *SimpleUDPSender) Send(msg []byte) error { - _, err := sender.udpConn.WriteToUDP(msg, sender.conn.RemoteUDPAddr()) - return err -} - -func (sender *SimpleUDPSender) Shutdown() error { - return nil -} - -func NewRawUDPSender(conn *LocalConnection) (*RawUDPSender, error) { - ipSocket, err := dialIP(conn) - if err != nil { - return nil, err - } - udpHeader := &layers.UDP{SrcPort: layers.UDPPort(Port)} - ipBuf := gopacket.NewSerializeBuffer() - opts := gopacket.SerializeOptions{ - FixLengths: true, - // UDP header is calculated with a phantom IP - // header. Yes, it's totally nuts. Thankfully, for UDP - // over IPv4, the checksum is optional. It's not - // optional for IPv6, but we'll ignore that for - // now. TODO - ComputeChecksums: false} - - return &RawUDPSender{ - ipBuf: ipBuf, - opts: opts, - udpHeader: udpHeader, - socket: ipSocket, - conn: conn}, nil -} - -func (sender *RawUDPSender) Send(msg []byte) error { - payload := gopacket.Payload(msg) - sender.udpHeader.DstPort = layers.UDPPort(sender.conn.RemoteUDPAddr().Port) - - err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload) - if err != nil { - return err - } - packet := sender.ipBuf.Bytes() - _, err = sender.socket.Write(packet) - if err == nil || PosixError(err) != syscall.EMSGSIZE { - return err - } - f, err := sender.socket.File() - if err != nil { - return err - } - defer f.Close() - fd := int(f.Fd()) - log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was", - len(packet), "bytes, payload was", len(msg), "bytes)") - pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU) - if err != nil { - return err - } - return MsgTooBigError{PMTU: pmtu} -} - -func (sender *RawUDPSender) Shutdown() error { - defer func() { sender.socket = nil }() - return sender.socket.Close() -} - -func dialIP(conn *LocalConnection) (*net.IPConn, error) { - ipLocalAddr, err := ipAddr(conn.TCPConn.LocalAddr()) - if err != nil { - return nil, err - } - ipRemoteAddr, err := ipAddr(conn.TCPConn.RemoteAddr()) - if err != nil { - return nil, err - } - ipSocket, err := net.DialIP("ip4:UDP", ipLocalAddr, ipRemoteAddr) - if err != nil { - return nil, err - } - f, err := ipSocket.File() - if err != nil { - return nil, err - } - defer f.Close() - fd := int(f.Fd()) - // This Makes sure all packets we send out have DF set on them. - err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO) - if err != nil { - return nil, err - } - return ipSocket, nil -} - -func ipAddr(addr net.Addr) (*net.IPAddr, error) { - host, _, err := net.SplitHostPort(addr.String()) - if err != nil { - return nil, err - } - return &net.IPAddr{ - IP: net.ParseIP(host), - Zone: ""}, nil -} diff --git a/router/udp_sender.go b/router/udp_sender.go new file mode 100644 index 00000000..6173dc8e --- /dev/null +++ b/router/udp_sender.go @@ -0,0 +1,116 @@ +package router + +import ( + "code.google.com/p/gopacket" + "code.google.com/p/gopacket/layers" + "log" + "net" + "syscall" +) + +func NewSimpleUDPSender(conn *LocalConnection) *SimpleUDPSender { + return &SimpleUDPSender{udpConn: conn.Router.UDPListener, conn: conn} +} + +func (sender *SimpleUDPSender) Send(msg []byte) error { + _, err := sender.udpConn.WriteToUDP(msg, sender.conn.RemoteUDPAddr()) + return err +} + +func (sender *SimpleUDPSender) Shutdown() error { + return nil +} + +func NewRawUDPSender(conn *LocalConnection) (*RawUDPSender, error) { + ipSocket, err := dialIP(conn) + if err != nil { + return nil, err + } + udpHeader := &layers.UDP{SrcPort: layers.UDPPort(Port)} + ipBuf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + // UDP header is calculated with a phantom IP + // header. Yes, it's totally nuts. Thankfully, for UDP + // over IPv4, the checksum is optional. It's not + // optional for IPv6, but we'll ignore that for + // now. TODO + ComputeChecksums: false} + + return &RawUDPSender{ + ipBuf: ipBuf, + opts: opts, + udpHeader: udpHeader, + socket: ipSocket, + conn: conn}, nil +} + +func (sender *RawUDPSender) Send(msg []byte) error { + payload := gopacket.Payload(msg) + sender.udpHeader.DstPort = layers.UDPPort(sender.conn.RemoteUDPAddr().Port) + + err := gopacket.SerializeLayers(sender.ipBuf, sender.opts, sender.udpHeader, &payload) + if err != nil { + return err + } + packet := sender.ipBuf.Bytes() + _, err = sender.socket.Write(packet) + if err == nil || PosixError(err) != syscall.EMSGSIZE { + return err + } + f, err := sender.socket.File() + if err != nil { + return err + } + defer f.Close() + fd := int(f.Fd()) + log.Println("EMSGSIZE on send, expecting PMTU update (IP packet was", + len(packet), "bytes, payload was", len(msg), "bytes)") + pmtu, err := syscall.GetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU) + if err != nil { + return err + } + return MsgTooBigError{PMTU: pmtu} +} + +func (sender *RawUDPSender) Shutdown() error { + defer func() { sender.socket = nil }() + return sender.socket.Close() +} + +func dialIP(conn *LocalConnection) (*net.IPConn, error) { + ipLocalAddr, err := ipAddr(conn.TCPConn.LocalAddr()) + if err != nil { + return nil, err + } + ipRemoteAddr, err := ipAddr(conn.TCPConn.RemoteAddr()) + if err != nil { + return nil, err + } + ipSocket, err := net.DialIP("ip4:UDP", ipLocalAddr, ipRemoteAddr) + if err != nil { + return nil, err + } + f, err := ipSocket.File() + if err != nil { + return nil, err + } + defer f.Close() + fd := int(f.Fd()) + // This Makes sure all packets we send out have DF set on them. + err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO) + if err != nil { + return nil, err + } + return ipSocket, nil +} + +func ipAddr(addr net.Addr) (*net.IPAddr, error) { + host, _, err := net.SplitHostPort(addr.String()) + if err != nil { + return nil, err + } + return &net.IPAddr{ + IP: net.ParseIP(host), + Zone: ""}, nil +}