David K. Bainbridge | 215e024 | 2017-09-05 23:18:24 -0700 | [diff] [blame] | 1 | // Copyright 2012 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 ipv4 |
| 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 | type rawOpt struct { |
| 17 | sync.RWMutex |
| 18 | cflags ControlFlags |
| 19 | } |
| 20 | |
| 21 | func (c *rawOpt) set(f ControlFlags) { c.cflags |= f } |
| 22 | func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f } |
| 23 | func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 } |
| 24 | |
| 25 | type ControlFlags uint |
| 26 | |
| 27 | const ( |
| 28 | FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet |
| 29 | FlagSrc // pass the source address on the received packet |
| 30 | FlagDst // pass the destination address on the received packet |
| 31 | FlagInterface // pass the interface index on the received packet |
| 32 | ) |
| 33 | |
| 34 | // A ControlMessage represents per packet basis IP-level socket options. |
| 35 | type ControlMessage struct { |
| 36 | // Receiving socket options: SetControlMessage allows to |
| 37 | // receive the options from the protocol stack using ReadFrom |
| 38 | // method of PacketConn or RawConn. |
| 39 | // |
| 40 | // Specifying socket options: ControlMessage for WriteTo |
| 41 | // method of PacketConn or RawConn allows to send the options |
| 42 | // to the protocol stack. |
| 43 | // |
| 44 | TTL int // time-to-live, receiving only |
| 45 | Src net.IP // source address, specifying only |
| 46 | Dst net.IP // destination address, receiving only |
| 47 | IfIndex int // interface index, must be 1 <= value when specifying |
| 48 | } |
| 49 | |
| 50 | func (cm *ControlMessage) String() string { |
| 51 | if cm == nil { |
| 52 | return "<nil>" |
| 53 | } |
| 54 | return fmt.Sprintf("ttl=%d src=%v dst=%v ifindex=%d", cm.TTL, cm.Src, cm.Dst, cm.IfIndex) |
| 55 | } |
| 56 | |
| 57 | // Marshal returns the binary encoding of cm. |
| 58 | func (cm *ControlMessage) Marshal() []byte { |
| 59 | if cm == nil { |
| 60 | return nil |
| 61 | } |
| 62 | var m socket.ControlMessage |
| 63 | if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex > 0) { |
| 64 | m = socket.NewControlMessage([]int{ctlOpts[ctlPacketInfo].length}) |
| 65 | } |
| 66 | if len(m) > 0 { |
| 67 | ctlOpts[ctlPacketInfo].marshal(m, cm) |
| 68 | } |
| 69 | return m |
| 70 | } |
| 71 | |
| 72 | // Parse parses b as a control message and stores the result in cm. |
| 73 | func (cm *ControlMessage) Parse(b []byte) error { |
| 74 | ms, err := socket.ControlMessage(b).Parse() |
| 75 | if err != nil { |
| 76 | return err |
| 77 | } |
| 78 | for _, m := range ms { |
| 79 | lvl, typ, l, err := m.ParseHeader() |
| 80 | if err != nil { |
| 81 | return err |
| 82 | } |
| 83 | if lvl != iana.ProtocolIP { |
| 84 | continue |
| 85 | } |
| 86 | switch { |
| 87 | case typ == ctlOpts[ctlTTL].name && l >= ctlOpts[ctlTTL].length: |
| 88 | ctlOpts[ctlTTL].parse(cm, m.Data(l)) |
| 89 | case typ == ctlOpts[ctlDst].name && l >= ctlOpts[ctlDst].length: |
| 90 | ctlOpts[ctlDst].parse(cm, m.Data(l)) |
| 91 | case typ == ctlOpts[ctlInterface].name && l >= ctlOpts[ctlInterface].length: |
| 92 | ctlOpts[ctlInterface].parse(cm, m.Data(l)) |
| 93 | case typ == ctlOpts[ctlPacketInfo].name && l >= ctlOpts[ctlPacketInfo].length: |
| 94 | ctlOpts[ctlPacketInfo].parse(cm, m.Data(l)) |
| 95 | } |
| 96 | } |
| 97 | return nil |
| 98 | } |
| 99 | |
| 100 | // NewControlMessage returns a new control message. |
| 101 | // |
| 102 | // The returned message is large enough for options specified by cf. |
| 103 | func NewControlMessage(cf ControlFlags) []byte { |
| 104 | opt := rawOpt{cflags: cf} |
| 105 | var l int |
| 106 | if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 { |
| 107 | l += socket.ControlMessageSpace(ctlOpts[ctlTTL].length) |
| 108 | } |
| 109 | if ctlOpts[ctlPacketInfo].name > 0 { |
| 110 | if opt.isset(FlagSrc | FlagDst | FlagInterface) { |
| 111 | l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length) |
| 112 | } |
| 113 | } else { |
| 114 | if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 { |
| 115 | l += socket.ControlMessageSpace(ctlOpts[ctlDst].length) |
| 116 | } |
| 117 | if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 { |
| 118 | l += socket.ControlMessageSpace(ctlOpts[ctlInterface].length) |
| 119 | } |
| 120 | } |
| 121 | var b []byte |
| 122 | if l > 0 { |
| 123 | b = make([]byte, l) |
| 124 | } |
| 125 | return b |
| 126 | } |
| 127 | |
| 128 | // Ancillary data socket options |
| 129 | const ( |
| 130 | ctlTTL = iota // header field |
| 131 | ctlSrc // header field |
| 132 | ctlDst // header field |
| 133 | ctlInterface // inbound or outbound interface |
| 134 | ctlPacketInfo // inbound or outbound packet path |
| 135 | ctlMax |
| 136 | ) |
| 137 | |
| 138 | // A ctlOpt represents a binding for ancillary data socket option. |
| 139 | type ctlOpt struct { |
| 140 | name int // option name, must be equal or greater than 1 |
| 141 | length int // option length |
| 142 | marshal func([]byte, *ControlMessage) []byte |
| 143 | parse func(*ControlMessage, []byte) |
| 144 | } |