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
5package ipv6
6
7import (
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
21type rawOpt struct {
22	sync.RWMutex
23	cflags ControlFlags
24}
25
26func (c *rawOpt) set(f ControlFlags)        { c.cflags |= f }
27func (c *rawOpt) clear(f ControlFlags)      { c.cflags &^= f }
28func (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.
32type ControlFlags uint
33
34const (
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
43const flagPacketInfo = FlagDst | FlagInterface
44
45// A ControlMessage represents per packet basis IP-level socket
46// options.
47type 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
65func (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.
73func (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.
119func (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.
149func 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
172const (
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.
182type 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}
188