github.com/vishvananda/netlink@v1.3.1/fou_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 package netlink 5 6 import ( 7 "encoding/binary" 8 "errors" 9 "log" 10 "net" 11 12 "github.com/vishvananda/netlink/nl" 13 "golang.org/x/sys/unix" 14 ) 15 16 const ( 17 FOU_GENL_NAME = "fou" 18 ) 19 20 const ( 21 FOU_CMD_UNSPEC uint8 = iota 22 FOU_CMD_ADD 23 FOU_CMD_DEL 24 FOU_CMD_GET 25 FOU_CMD_MAX = FOU_CMD_GET 26 ) 27 28 const ( 29 FOU_ATTR_UNSPEC = iota 30 FOU_ATTR_PORT 31 FOU_ATTR_AF 32 FOU_ATTR_IPPROTO 33 FOU_ATTR_TYPE 34 FOU_ATTR_REMCSUM_NOPARTIAL 35 FOU_ATTR_LOCAL_V4 36 FOU_ATTR_LOCAL_V6 37 FOU_ATTR_PEER_V4 38 FOU_ATTR_PEER_V6 39 FOU_ATTR_PEER_PORT 40 FOU_ATTR_IFINDEX 41 FOU_ATTR_MAX = FOU_ATTR_REMCSUM_NOPARTIAL 42 ) 43 44 const ( 45 FOU_ENCAP_UNSPEC = iota 46 FOU_ENCAP_DIRECT 47 FOU_ENCAP_GUE 48 FOU_ENCAP_MAX = FOU_ENCAP_GUE 49 ) 50 51 var fouFamilyId int 52 53 func FouFamilyId() (int, error) { 54 if fouFamilyId != 0 { 55 return fouFamilyId, nil 56 } 57 58 fam, err := GenlFamilyGet(FOU_GENL_NAME) 59 if err != nil { 60 return -1, err 61 } 62 63 fouFamilyId = int(fam.ID) 64 return fouFamilyId, nil 65 } 66 67 func FouAdd(f Fou) error { 68 return pkgHandle.FouAdd(f) 69 } 70 71 func (h *Handle) FouAdd(f Fou) error { 72 fam_id, err := FouFamilyId() 73 if err != nil { 74 return err 75 } 76 77 // setting ip protocol conflicts with encapsulation type GUE 78 if f.EncapType == FOU_ENCAP_GUE && f.Protocol != 0 { 79 return errors.New("GUE encapsulation doesn't specify an IP protocol") 80 } 81 82 req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK) 83 84 // int to byte for port 85 bp := make([]byte, 2) 86 binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port)) 87 88 attrs := []*nl.RtAttr{ 89 nl.NewRtAttr(FOU_ATTR_PORT, bp), 90 nl.NewRtAttr(FOU_ATTR_TYPE, []byte{uint8(f.EncapType)}), 91 nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}), 92 nl.NewRtAttr(FOU_ATTR_IPPROTO, []byte{uint8(f.Protocol)}), 93 } 94 raw := []byte{FOU_CMD_ADD, 1, 0, 0} 95 for _, a := range attrs { 96 raw = append(raw, a.Serialize()...) 97 } 98 99 req.AddRawData(raw) 100 101 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 102 return err 103 } 104 105 func FouDel(f Fou) error { 106 return pkgHandle.FouDel(f) 107 } 108 109 func (h *Handle) FouDel(f Fou) error { 110 fam_id, err := FouFamilyId() 111 if err != nil { 112 return err 113 } 114 115 req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK) 116 117 // int to byte for port 118 bp := make([]byte, 2) 119 binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port)) 120 121 attrs := []*nl.RtAttr{ 122 nl.NewRtAttr(FOU_ATTR_PORT, bp), 123 nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}), 124 } 125 raw := []byte{FOU_CMD_DEL, 1, 0, 0} 126 for _, a := range attrs { 127 raw = append(raw, a.Serialize()...) 128 } 129 130 req.AddRawData(raw) 131 132 _, err = req.Execute(unix.NETLINK_GENERIC, 0) 133 if err != nil { 134 return err 135 } 136 137 return nil 138 } 139 140 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 141 // or incomplete. 142 func FouList(fam int) ([]Fou, error) { 143 return pkgHandle.FouList(fam) 144 } 145 146 // If the returned error is [ErrDumpInterrupted], results may be inconsistent 147 // or incomplete. 148 func (h *Handle) FouList(fam int) ([]Fou, error) { 149 fam_id, err := FouFamilyId() 150 if err != nil { 151 return nil, err 152 } 153 154 req := h.newNetlinkRequest(fam_id, unix.NLM_F_DUMP) 155 156 attrs := []*nl.RtAttr{ 157 nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(fam)}), 158 } 159 raw := []byte{FOU_CMD_GET, 1, 0, 0} 160 for _, a := range attrs { 161 raw = append(raw, a.Serialize()...) 162 } 163 164 req.AddRawData(raw) 165 166 msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) 167 if executeErr != nil && !errors.Is(err, ErrDumpInterrupted) { 168 return nil, executeErr 169 } 170 171 fous := make([]Fou, 0, len(msgs)) 172 for _, m := range msgs { 173 f, err := deserializeFouMsg(m) 174 if err != nil { 175 return fous, err 176 } 177 178 fous = append(fous, f) 179 } 180 181 return fous, executeErr 182 } 183 184 func deserializeFouMsg(msg []byte) (Fou, error) { 185 fou := Fou{} 186 187 for attr := range nl.ParseAttributes(msg[4:]) { 188 switch attr.Type { 189 case FOU_ATTR_AF: 190 fou.Family = int(attr.Value[0]) 191 case FOU_ATTR_PORT: 192 fou.Port = int(networkOrder.Uint16(attr.Value)) 193 case FOU_ATTR_IPPROTO: 194 fou.Protocol = int(attr.Value[0]) 195 case FOU_ATTR_TYPE: 196 fou.EncapType = int(attr.Value[0]) 197 case FOU_ATTR_LOCAL_V4, FOU_ATTR_LOCAL_V6: 198 fou.Local = net.IP(attr.Value) 199 case FOU_ATTR_PEER_V4, FOU_ATTR_PEER_V6: 200 fou.Peer = net.IP(attr.Value) 201 case FOU_ATTR_PEER_PORT: 202 fou.PeerPort = int(networkOrder.Uint16(attr.Value)) 203 case FOU_ATTR_IFINDEX: 204 fou.IfIndex = int(native.Uint16(attr.Value)) 205 default: 206 log.Printf("unknown fou attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) 207 } 208 } 209 210 return fou, nil 211 }