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