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  }