From 625edb9f2864c1b3aebc6b4c9828197131c2e003 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 31 Aug 2018 10:22:06 -0700 Subject: ipv6: ICMP support This CL does NDP link-address discovery for IPv6. It includes several small changes necessary to get linux to talk to this implementation. In particular, a hop limit of 255 is necessary for ICMPv6. PiperOrigin-RevId: 211103930 Change-Id: If25370ab84c6b1decfb15de917f3b0020f2c4e0e --- pkg/tcpip/transport/ping/endpoint.go | 38 ++++++++++++++++++++++++++++++------ pkg/tcpip/transport/ping/protocol.go | 16 +++++++++++---- 2 files changed, 44 insertions(+), 10 deletions(-) (limited to 'pkg/tcpip/transport/ping') diff --git a/pkg/tcpip/transport/ping/endpoint.go b/pkg/tcpip/transport/ping/endpoint.go index f097ac057..7e10c0aae 100644 --- a/pkg/tcpip/transport/ping/endpoint.go +++ b/pkg/tcpip/transport/ping/endpoint.go @@ -56,6 +56,7 @@ type endpoint struct { // change throughout the lifetime of the endpoint. stack *stack.Stack `state:"manual"` netProto tcpip.NetworkProtocolNumber + transProto tcpip.TransportProtocolNumber waiterQueue *waiter.Queue // The following fields are used to manage the receive queue, and are @@ -81,10 +82,11 @@ type endpoint struct { route stack.Route `state:"manual"` } -func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) *endpoint { +func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) *endpoint { return &endpoint{ stack: stack, netProto: netProto, + transProto: transProto, waiterQueue: waiterQueue, rcvBufSizeMax: 32 * 1024, sndBufSize: 32 * 1024, @@ -98,7 +100,7 @@ func (e *endpoint) Close() { e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite switch e.state { case stateBound, stateConnected: - e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, ProtocolNumber4, e.id) + e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.transProto, e.id) } // Close the receive list and drain it. @@ -297,7 +299,7 @@ func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, *tc err = sendPing4(route, e.id.LocalPort, v) case header.IPv6ProtocolNumber: - // TODO: Support IPv6. + err = sendPing6(route, e.id.LocalPort, v) } return uintptr(len(v)), err @@ -385,6 +387,30 @@ func sendPing4(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error { return r.WritePacket(&hdr, data, header.ICMPv4ProtocolNumber) } +func sendPing6(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error { + if len(data) < header.ICMPv6EchoMinimumSize { + return tcpip.ErrInvalidEndpointState + } + + // Set the ident. Sequence number is provided by the user. + binary.BigEndian.PutUint16(data[header.ICMPv6MinimumSize:], ident) + + hdr := buffer.NewPrependable(header.ICMPv6EchoMinimumSize + int(r.MaxHeaderLength())) + + icmpv6 := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize)) + copy(icmpv6, data) + data = data[header.ICMPv6EchoMinimumSize:] + + if icmpv6.Type() != header.ICMPv6EchoRequest || icmpv6.Code() != 0 { + return tcpip.ErrInvalidEndpointState + } + + icmpv6.SetChecksum(0) + icmpv6.SetChecksum(^header.Checksum(icmpv6, header.Checksum(data, 0))) + + return r.WritePacket(&hdr, data, header.ICMPv6ProtocolNumber) +} + func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (tcpip.NetworkProtocolNumber, *tcpip.Error) { netProto := e.netProto if header.IsV4MappedAddress(addr.Addr) { @@ -508,14 +534,14 @@ func (e *endpoint) registerWithStack(nicid tcpip.NICID, netProtos []tcpip.Networ if id.LocalPort != 0 { // The endpoint already has a local port, just attempt to // register it. - err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber4, id, e) + err := e.stack.RegisterTransportEndpoint(nicid, netProtos, e.transProto, id, e) return id, err } // We need to find a port for the endpoint. _, err := e.stack.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) { id.LocalPort = p - err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber4, id, e) + err := e.stack.RegisterTransportEndpoint(nicid, netProtos, e.transProto, id, e) switch err { case nil: return true, nil @@ -564,7 +590,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error if commit != nil { if err := commit(); err != nil { // Unregister, the commit failed. - e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber4, id) + e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, e.transProto, id) return err } } diff --git a/pkg/tcpip/transport/ping/protocol.go b/pkg/tcpip/transport/ping/protocol.go index 8a8192064..b885f3627 100644 --- a/pkg/tcpip/transport/ping/protocol.go +++ b/pkg/tcpip/transport/ping/protocol.go @@ -71,7 +71,7 @@ func (p *protocol) NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtoco if netProto != p.netProto() { return nil, tcpip.ErrUnknownProtocol } - return newEndpoint(stack, netProto, waiterQueue), nil + return newEndpoint(stack, netProto, p.number, waiterQueue), nil } // MinimumPacketSize returns the minimum valid ping packet size. @@ -87,8 +87,14 @@ func (p *protocol) MinimumPacketSize() int { // ParsePorts returns the source and destination ports stored in the given ping // packet. -func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) { - return 0, binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize:]), nil +func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) { + switch p.number { + case ProtocolNumber4: + return 0, binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize:]), nil + case ProtocolNumber6: + return 0, binary.BigEndian.Uint16(v[header.ICMPv6MinimumSize:]), nil + } + panic(fmt.Sprint("unknown protocol number: ", p.number)) } // HandleUnknownDestinationPacket handles packets targeted at this protocol but @@ -112,5 +118,7 @@ func init() { return &protocol{ProtocolNumber4} }) - // TODO: Support IPv6. + stack.RegisterTransportProtocolFactory(ProtocolName6, func() stack.TransportProtocol { + return &protocol{ProtocolNumber6} + }) } -- cgit v1.2.3