blob: 672f2d34d8af7851e8239a2070986b7da69cd3b7 [file] [log] [blame]
Mikio Harad2e5a122012-09-26 21:03:09 +09001// 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
5package ipv4
6
7import (
8 "errors"
9 "fmt"
10 "net"
11 "runtime"
12 "syscall"
13 "unsafe"
14)
15
16var (
17 errMissingAddress = errors.New("missing address")
18 errMissingHeader = errors.New("missing header")
19 errHeaderTooShort = errors.New("header too short")
20 errBufferTooShort = errors.New("buffer too short")
21 errInvalidConnType = errors.New("invalid conn type")
22)
23
24// References:
25//
26// RFC 791 Internet Protocol
27// http://tools.ietf.org/html/rfc791
28// RFC 1112 Host Extensions for IP Multicasting
29// http://tools.ietf.org/html/rfc1112
30// RFC 1122 Requirements for Internet Hosts
31// http://tools.ietf.org/html/rfc1122
Mikio Harad2e5a122012-09-26 21:03:09 +090032
33const (
34 Version = 4 // protocol version
35 HeaderLen = 20 // header length without extension headers
36 maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields
37)
38
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +090039const (
40 posTOS = 1 // type-of-service
41 posTotalLen = 2 // packet total length
42 posID = 4 // identification
43 posFragOff = 6 // fragment offset
44 posTTL = 8 // time-to-live
45 posProtocol = 9 // next protocol
46 posChecksum = 10 // checksum
47 posSrc = 12 // source address
48 posDst = 16 // destination address
49)
50
51type HeaderFlags int
Mikio Harad2e5a122012-09-26 21:03:09 +090052
53const (
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +090054 MoreFragments HeaderFlags = 1 << iota // more fragments flag
55 DontFragment // don't fragment flag
Mikio Harad2e5a122012-09-26 21:03:09 +090056)
57
58// A Header represents an IPv4 header.
59type Header struct {
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +090060 Version int // protocol version
61 Len int // header length
62 TOS int // type-of-service
63 TotalLen int // packet total length
64 ID int // identification
65 Flags HeaderFlags // flags
66 FragOff int // fragment offset
67 TTL int // time-to-live
68 Protocol int // next protocol
69 Checksum int // checksum
70 Src net.IP // source address
71 Dst net.IP // destination address
72 Options []byte // options, extension headers
Mikio Harad2e5a122012-09-26 21:03:09 +090073}
74
75func (h *Header) String() string {
76 if h == nil {
77 return "<nil>"
78 }
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +090079 return fmt.Sprintf("ver: %v, hdrlen: %v, tos: %#x, totallen: %v, id: %#x, flags: %#x, fragoff: %#x, ttl: %v, proto: %v, 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)
Mikio Harad2e5a122012-09-26 21:03:09 +090080}
81
82// Please refer to the online manual; IP(4) on Darwin, FreeBSD and
83// OpenBSD. IP(7) on Linux.
Mikio Haraaf425e02012-11-06 22:00:17 +090084const supportsNewIPInput = runtime.GOOS == "linux" || runtime.GOOS == "openbsd"
Mikio Harad2e5a122012-09-26 21:03:09 +090085
86// Marshal returns the binary encoding of the IPv4 header h.
87func (h *Header) Marshal() ([]byte, error) {
88 if h == nil {
89 return nil, syscall.EINVAL
90 }
91 if h.Len < HeaderLen {
92 return nil, errHeaderTooShort
93 }
94 hdrlen := HeaderLen + len(h.Options)
95 b := make([]byte, hdrlen)
96 b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f))
97 b[posTOS] = byte(h.TOS)
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +090098 flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13)
Mikio Harad2e5a122012-09-26 21:03:09 +090099 if supportsNewIPInput {
100 b[posTotalLen], b[posTotalLen+1] = byte(h.TotalLen>>8), byte(h.TotalLen)
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +0900101 b[posFragOff], b[posFragOff+1] = byte(flagsAndFragOff>>8), byte(flagsAndFragOff)
Mikio Harad2e5a122012-09-26 21:03:09 +0900102 } else {
103 *(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])) = uint16(h.TotalLen)
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +0900104 *(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])) = uint16(flagsAndFragOff)
Mikio Harad2e5a122012-09-26 21:03:09 +0900105 }
106 b[posID], b[posID+1] = byte(h.ID>>8), byte(h.ID)
107 b[posTTL] = byte(h.TTL)
108 b[posProtocol] = byte(h.Protocol)
109 b[posChecksum], b[posChecksum+1] = byte(h.Checksum>>8), byte(h.Checksum)
110 if ip := h.Src.To4(); ip != nil {
Mikio Harab38392a2013-02-14 07:01:07 +0900111 copy(b[posSrc:posSrc+net.IPv4len], ip[:net.IPv4len])
Mikio Harad2e5a122012-09-26 21:03:09 +0900112 }
113 if ip := h.Dst.To4(); ip != nil {
Mikio Harab38392a2013-02-14 07:01:07 +0900114 copy(b[posDst:posDst+net.IPv4len], ip[:net.IPv4len])
Mikio Harad2e5a122012-09-26 21:03:09 +0900115 } else {
116 return nil, errMissingAddress
117 }
118 if len(h.Options) > 0 {
119 copy(b[HeaderLen:], h.Options)
120 }
121 return b, nil
122}
123
Mikio Hara28a4bd92014-01-29 10:23:52 +0900124// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
125var freebsdVersion uint32
126
Mikio Harad2e5a122012-09-26 21:03:09 +0900127// ParseHeader parses b as an IPv4 header.
128func ParseHeader(b []byte) (*Header, error) {
129 if len(b) < HeaderLen {
130 return nil, errHeaderTooShort
131 }
Mikio Harab38392a2013-02-14 07:01:07 +0900132 hdrlen := int(b[0]&0x0f) << 2
Mikio Harad2e5a122012-09-26 21:03:09 +0900133 if hdrlen > len(b) {
134 return nil, errBufferTooShort
135 }
136 h := &Header{}
137 h.Version = int(b[0] >> 4)
138 h.Len = hdrlen
139 h.TOS = int(b[posTOS])
140 if supportsNewIPInput {
141 h.TotalLen = int(b[posTotalLen])<<8 | int(b[posTotalLen+1])
142 h.FragOff = int(b[posFragOff])<<8 | int(b[posFragOff+1])
143 } else {
144 h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0])))
Mikio Hara28a4bd92014-01-29 10:23:52 +0900145 if runtime.GOOS != "freebsd" || freebsdVersion < 1000000 {
146 h.TotalLen += hdrlen
147 }
Mikio Harad2e5a122012-09-26 21:03:09 +0900148 h.FragOff = int(*(*uint16)(unsafe.Pointer(&b[posFragOff : posFragOff+1][0])))
149 }
Marko Juhani Silokunnasc2e426e2013-10-08 06:52:49 +0900150 h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13
151 h.FragOff = h.FragOff & 0x1fff
Mikio Harad2e5a122012-09-26 21:03:09 +0900152 h.ID = int(b[posID])<<8 | int(b[posID+1])
153 h.TTL = int(b[posTTL])
154 h.Protocol = int(b[posProtocol])
155 h.Checksum = int(b[posChecksum])<<8 | int(b[posChecksum+1])
156 h.Src = net.IPv4(b[posSrc], b[posSrc+1], b[posSrc+2], b[posSrc+3])
157 h.Dst = net.IPv4(b[posDst], b[posDst+1], b[posDst+2], b[posDst+3])
158 if hdrlen-HeaderLen > 0 {
159 h.Options = make([]byte, hdrlen-HeaderLen)
160 copy(h.Options, b[HeaderLen:])
161 }
162 return h, nil
163}