1
2
3
4
5 package ipv4
6
7 import (
8 "encoding/binary"
9 "fmt"
10 "net"
11 "runtime"
12
13 "golang.org/x/net/internal/socket"
14 )
15
16 const (
17 Version = 4
18 HeaderLen = 20
19 )
20
21 type HeaderFlags int
22
23 const (
24 MoreFragments HeaderFlags = 1 << iota
25 DontFragment
26 )
27
28
29 type Header struct {
30 Version int
31 Len int
32 TOS int
33 TotalLen int
34 ID int
35 Flags HeaderFlags
36 FragOff int
37 TTL int
38 Protocol int
39 Checksum int
40 Src net.IP
41 Dst net.IP
42 Options []byte
43 }
44
45 func (h *Header) String() string {
46 if h == nil {
47 return "<nil>"
48 }
49 return fmt.Sprintf("ver=%d hdrlen=%d tos=%#x totallen=%d id=%#x flags=%#x fragoff=%#x ttl=%d proto=%d cksum=%#x src=%v dst=%v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst)
50 }
51
52
53
54
55
56
57 func (h *Header) Marshal() ([]byte, error) {
58 if h == nil {
59 return nil, errNilHeader
60 }
61 if h.Len < HeaderLen {
62 return nil, errHeaderTooShort
63 }
64 hdrlen := HeaderLen + len(h.Options)
65 b := make([]byte, hdrlen)
66 b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
67 b[1] = byte(h.TOS)
68 flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
69 switch runtime.GOOS {
70 case "darwin", "ios", "dragonfly", "netbsd":
71 socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
72 socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
73 case "freebsd":
74 if freebsdVersion < 1100000 {
75 socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen))
76 socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
77 } else {
78 binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
79 binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
80 }
81 default:
82 binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen))
83 binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff))
84 }
85 binary.BigEndian.PutUint16(b[4:6], uint16(h.ID))
86 b[8] = byte(h.TTL)
87 b[9] = byte(h.Protocol)
88 binary.BigEndian.PutUint16(b[10:12], uint16(h.Checksum))
89 if ip := h.Src.To4(); ip != nil {
90 copy(b[12:16], ip[:net.IPv4len])
91 }
92 if ip := h.Dst.To4(); ip != nil {
93 copy(b[16:20], ip[:net.IPv4len])
94 } else {
95 return nil, errMissingAddress
96 }
97 if len(h.Options) > 0 {
98 copy(b[HeaderLen:], h.Options)
99 }
100 return b, nil
101 }
102
103
104
105
106
107
108 func (h *Header) Parse(b []byte) error {
109 if h == nil || b == nil {
110 return errNilHeader
111 }
112 if len(b) < HeaderLen {
113 return errHeaderTooShort
114 }
115 hdrlen := int(b[0]&0x0f) << 2
116 if len(b) < hdrlen {
117 return errExtHeaderTooShort
118 }
119 h.Version = int(b[0] >> 4)
120 h.Len = hdrlen
121 h.TOS = int(b[1])
122 h.ID = int(binary.BigEndian.Uint16(b[4:6]))
123 h.TTL = int(b[8])
124 h.Protocol = int(b[9])
125 h.Checksum = int(binary.BigEndian.Uint16(b[10:12]))
126 h.Src = net.IPv4(b[12], b[13], b[14], b[15])
127 h.Dst = net.IPv4(b[16], b[17], b[18], b[19])
128 switch runtime.GOOS {
129 case "darwin", "ios", "dragonfly", "netbsd":
130 h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + hdrlen
131 h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
132 case "freebsd":
133 if freebsdVersion < 1100000 {
134 h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4]))
135 if freebsdVersion < 1000000 {
136 h.TotalLen += hdrlen
137 }
138 h.FragOff = int(socket.NativeEndian.Uint16(b[6:8]))
139 } else {
140 h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
141 h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
142 }
143 default:
144 h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
145 h.FragOff = int(binary.BigEndian.Uint16(b[6:8]))
146 }
147 h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
148 h.FragOff = h.FragOff & 0x1fff
149 optlen := hdrlen - HeaderLen
150 if optlen > 0 && len(b) >= hdrlen {
151 if cap(h.Options) < optlen {
152 h.Options = make([]byte, optlen)
153 } else {
154 h.Options = h.Options[:optlen]
155 }
156 copy(h.Options, b[HeaderLen:hdrlen])
157 }
158 return nil
159 }
160
161
162
163
164
165
166 func ParseHeader(b []byte) (*Header, error) {
167 h := new(Header)
168 if err := h.Parse(b); err != nil {
169 return nil, err
170 }
171 return h, nil
172 }
173
View as plain text