Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [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 ( |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 8 | "os" |
| 9 | "syscall" |
| 10 | "unsafe" |
| 11 | ) |
| 12 | |
| 13 | // Linux provides a convenient path control option IP_PKTINFO that |
| 14 | // contains IP_SENDSRCADDR, IP_RECVDSTADDR, IP_RECVIF and IP_SENDIF. |
| 15 | const pktinfo = FlagSrc | FlagDst | FlagInterface |
| 16 | |
| 17 | func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 18 | opt.Lock() |
| 19 | defer opt.Unlock() |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 20 | if cf&FlagTTL != 0 { |
| 21 | if err := setIPv4ReceiveTTL(fd, on); err != nil { |
| 22 | return err |
| 23 | } |
| 24 | if on { |
| 25 | opt.set(FlagTTL) |
| 26 | } else { |
| 27 | opt.clear(FlagTTL) |
| 28 | } |
| 29 | } |
| 30 | if cf&pktinfo != 0 { |
| 31 | if err := setIPv4PacketInfo(fd, on); err != nil { |
| 32 | return err |
| 33 | } |
| 34 | if on { |
| 35 | opt.set(cf & pktinfo) |
| 36 | } else { |
| 37 | opt.clear(cf & pktinfo) |
| 38 | } |
| 39 | } |
| 40 | return nil |
| 41 | } |
| 42 | |
| 43 | func newControlMessage(opt *rawOpt) (oob []byte) { |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 44 | opt.Lock() |
| 45 | defer opt.Unlock() |
| 46 | l, off := 0, 0 |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 47 | if opt.isset(FlagTTL) { |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 48 | l += syscall.CmsgSpace(1) |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 49 | } |
| 50 | if opt.isset(pktinfo) { |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 51 | l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) |
| 52 | } |
| 53 | if l > 0 { |
| 54 | oob = make([]byte, l) |
| 55 | if opt.isset(FlagTTL) { |
| 56 | m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) |
| 57 | m.Level = ianaProtocolIP |
| 58 | m.Type = syscall.IP_RECVTTL |
| 59 | m.SetLen(syscall.CmsgLen(1)) |
| 60 | off += syscall.CmsgSpace(1) |
| 61 | } |
| 62 | if opt.isset(pktinfo) { |
| 63 | m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) |
| 64 | m.Level = ianaProtocolIP |
| 65 | m.Type = syscall.IP_PKTINFO |
| 66 | m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) |
| 67 | off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) |
| 68 | } |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 69 | } |
| 70 | return |
| 71 | } |
| 72 | |
| 73 | func parseControlMessage(b []byte) (*ControlMessage, error) { |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 74 | if len(b) == 0 { |
| 75 | return nil, nil |
| 76 | } |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 77 | cmsgs, err := syscall.ParseSocketControlMessage(b) |
| 78 | if err != nil { |
| 79 | return nil, os.NewSyscallError("parse socket control message", err) |
| 80 | } |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 81 | cm := &ControlMessage{} |
| 82 | for _, m := range cmsgs { |
Mikio Hara | 8108b4b | 2013-06-07 14:52:58 +0900 | [diff] [blame] | 83 | if m.Header.Level != ianaProtocolIP { |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 84 | continue |
| 85 | } |
| 86 | switch m.Header.Type { |
| 87 | case syscall.IP_TTL: |
| 88 | cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) |
| 89 | case syscall.IP_PKTINFO: |
| 90 | pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&m.Data[0])) |
| 91 | cm.IfIndex = int(pi.Ifindex) |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 92 | cm.Dst = pi.Addr[:] |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 93 | } |
| 94 | } |
| 95 | return cm, nil |
| 96 | } |
| 97 | |
| 98 | func marshalControlMessage(cm *ControlMessage) (oob []byte) { |
| 99 | if cm == nil { |
| 100 | return |
| 101 | } |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 102 | l, off := 0, 0 |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 103 | pion := false |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 104 | if cm.Src.To4() != nil || cm.IfIndex != 0 { |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 105 | pion = true |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 106 | l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 107 | } |
Mikio Hara | ecb7ecd | 2013-06-17 00:40:07 +0900 | [diff] [blame] | 108 | if l > 0 { |
| 109 | oob = make([]byte, l) |
| 110 | if pion { |
| 111 | m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) |
| 112 | m.Level = ianaProtocolIP |
| 113 | m.Type = syscall.IP_PKTINFO |
| 114 | m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) |
| 115 | pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) |
| 116 | if ip := cm.Src.To4(); ip != nil { |
| 117 | copy(pi.Addr[:], ip) |
| 118 | } |
| 119 | if cm.IfIndex != 0 { |
| 120 | pi.Ifindex = int32(cm.IfIndex) |
| 121 | } |
| 122 | off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) |
| 123 | } |
Mikio Hara | d2e5a12 | 2012-09-26 21:03:09 +0900 | [diff] [blame] | 124 | } |
| 125 | return |
| 126 | } |