golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv6/control.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ipv6
     6  
     7  import (
     8  	"fmt"
     9  	"net"
    10  	"sync"
    11  
    12  	"golang.org/x/net/internal/iana"
    13  	"golang.org/x/net/internal/socket"
    14  )
    15  
    16  // Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the
    17  // former still support RFC 2292 only. Please be aware that almost
    18  // all protocol implementations prohibit using a combination of RFC
    19  // 2292 and RFC 3542 for some practical reasons.
    20  
    21  type rawOpt struct {
    22  	sync.RWMutex
    23  	cflags ControlFlags
    24  }
    25  
    26  func (c *rawOpt) set(f ControlFlags)        { c.cflags |= f }
    27  func (c *rawOpt) clear(f ControlFlags)      { c.cflags &^= f }
    28  func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 }
    29  
    30  // A ControlFlags represents per packet basis IP-level socket option
    31  // control flags.
    32  type ControlFlags uint
    33  
    34  const (
    35  	FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet
    36  	FlagHopLimit                              // pass the hop limit on the received packet
    37  	FlagSrc                                   // pass the source address on the received packet
    38  	FlagDst                                   // pass the destination address on the received packet
    39  	FlagInterface                             // pass the interface index on the received packet
    40  	FlagPathMTU                               // pass the path MTU on the received packet path
    41  )
    42  
    43  const flagPacketInfo = FlagDst | FlagInterface
    44  
    45  // A ControlMessage represents per packet basis IP-level socket
    46  // options.
    47  type ControlMessage struct {
    48  	// Receiving socket options: SetControlMessage allows to
    49  	// receive the options from the protocol stack using ReadFrom
    50  	// method of PacketConn.
    51  	//
    52  	// Specifying socket options: ControlMessage for WriteTo
    53  	// method of PacketConn allows to send the options to the
    54  	// protocol stack.
    55  	//
    56  	TrafficClass int    // traffic class, must be 1 <= value <= 255 when specifying
    57  	HopLimit     int    // hop limit, must be 1 <= value <= 255 when specifying
    58  	Src          net.IP // source address, specifying only
    59  	Dst          net.IP // destination address, receiving only
    60  	IfIndex      int    // interface index, must be 1 <= value when specifying
    61  	NextHop      net.IP // next hop address, specifying only
    62  	MTU          int    // path MTU, receiving only
    63  }
    64  
    65  func (cm *ControlMessage) String() string {
    66  	if cm == nil {
    67  		return "<nil>"
    68  	}
    69  	return fmt.Sprintf("tclass=%#x hoplim=%d src=%v dst=%v ifindex=%d nexthop=%v mtu=%d", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU)
    70  }
    71  
    72  // Marshal returns the binary encoding of cm.
    73  func (cm *ControlMessage) Marshal() []byte {
    74  	if cm == nil {
    75  		return nil
    76  	}
    77  	var l int
    78  	tclass := false
    79  	if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 {
    80  		tclass = true
    81  		l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length)
    82  	}
    83  	hoplimit := false
    84  	if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 {
    85  		hoplimit = true
    86  		l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length)
    87  	}
    88  	pktinfo := false
    89  	if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0) {
    90  		pktinfo = true
    91  		l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length)
    92  	}
    93  	nexthop := false
    94  	if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil {
    95  		nexthop = true
    96  		l += socket.ControlMessageSpace(ctlOpts[ctlNextHop].length)
    97  	}
    98  	var b []byte
    99  	if l > 0 {
   100  		b = make([]byte, l)
   101  		bb := b
   102  		if tclass {
   103  			bb = ctlOpts[ctlTrafficClass].marshal(bb, cm)
   104  		}
   105  		if hoplimit {
   106  			bb = ctlOpts[ctlHopLimit].marshal(bb, cm)
   107  		}
   108  		if pktinfo {
   109  			bb = ctlOpts[ctlPacketInfo].marshal(bb, cm)
   110  		}
   111  		if nexthop {
   112  			bb = ctlOpts[ctlNextHop].marshal(bb, cm)
   113  		}
   114  	}
   115  	return b
   116  }
   117  
   118  // Parse parses b as a control message and stores the result in cm.
   119  func (cm *ControlMessage) Parse(b []byte) error {
   120  	ms, err := socket.ControlMessage(b).Parse()
   121  	if err != nil {
   122  		return err
   123  	}
   124  	for _, m := range ms {
   125  		lvl, typ, l, err := m.ParseHeader()
   126  		if err != nil {
   127  			return err
   128  		}
   129  		if lvl != iana.ProtocolIPv6 {
   130  			continue
   131  		}
   132  		switch {
   133  		case typ == ctlOpts[ctlTrafficClass].name && l >= ctlOpts[ctlTrafficClass].length:
   134  			ctlOpts[ctlTrafficClass].parse(cm, m.Data(l))
   135  		case typ == ctlOpts[ctlHopLimit].name && l >= ctlOpts[ctlHopLimit].length:
   136  			ctlOpts[ctlHopLimit].parse(cm, m.Data(l))
   137  		case typ == ctlOpts[ctlPacketInfo].name && l >= ctlOpts[ctlPacketInfo].length:
   138  			ctlOpts[ctlPacketInfo].parse(cm, m.Data(l))
   139  		case typ == ctlOpts[ctlPathMTU].name && l >= ctlOpts[ctlPathMTU].length:
   140  			ctlOpts[ctlPathMTU].parse(cm, m.Data(l))
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  // NewControlMessage returns a new control message.
   147  //
   148  // The returned message is large enough for options specified by cf.
   149  func NewControlMessage(cf ControlFlags) []byte {
   150  	opt := rawOpt{cflags: cf}
   151  	var l int
   152  	if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 {
   153  		l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length)
   154  	}
   155  	if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 {
   156  		l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length)
   157  	}
   158  	if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 {
   159  		l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length)
   160  	}
   161  	if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 {
   162  		l += socket.ControlMessageSpace(ctlOpts[ctlPathMTU].length)
   163  	}
   164  	var b []byte
   165  	if l > 0 {
   166  		b = make([]byte, l)
   167  	}
   168  	return b
   169  }
   170  
   171  // Ancillary data socket options
   172  const (
   173  	ctlTrafficClass = iota // header field
   174  	ctlHopLimit            // header field
   175  	ctlPacketInfo          // inbound or outbound packet path
   176  	ctlNextHop             // nexthop
   177  	ctlPathMTU             // path mtu
   178  	ctlMax
   179  )
   180  
   181  // A ctlOpt represents a binding for ancillary data socket option.
   182  type ctlOpt struct {
   183  	name    int // option name, must be equal or greater than 1
   184  	length  int // option length
   185  	marshal func([]byte, *ControlMessage) []byte
   186  	parse   func(*ControlMessage, []byte)
   187  }