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