|
| 1 | +package netlink |
| 2 | + |
| 3 | +import ( |
| 4 | + "errors" |
| 5 | + "fmt" |
| 6 | + "syscall" |
| 7 | + |
| 8 | + "github.com/vishvananda/netlink/nl" |
| 9 | + "golang.org/x/sys/unix" |
| 10 | +) |
| 11 | + |
| 12 | +// NetDevQueueType identifies the direction of a netdev queue. |
| 13 | +type NetDevQueueType uint32 |
| 14 | + |
| 15 | +const ( |
| 16 | + NetDevQueueTypeRx NetDevQueueType = nl.NETDEV_QUEUE_TYPE_RX |
| 17 | + NetDevQueueTypeTx NetDevQueueType = nl.NETDEV_QUEUE_TYPE_TX |
| 18 | +) |
| 19 | + |
| 20 | +// NetDevQueueID identifies a single queue on a netdevice by its id and type. |
| 21 | +type NetDevQueueID struct { |
| 22 | + ID uint32 |
| 23 | + Type NetDevQueueType |
| 24 | +} |
| 25 | + |
| 26 | +// NetDevQueueLease describes the binding of a virtual netdev queue to a real |
| 27 | +// queue on a physical netdevice. A leased queue acts as a proxy: memory |
| 28 | +// provider (io_uring zero-copy, devmem) and AF_XDP operations issued against |
| 29 | +// the virtual queue are forwarded to the physical queue named here. |
| 30 | +type NetDevQueueLease struct { |
| 31 | + // IfIndex is the physical netdevice the queue is leased from. |
| 32 | + IfIndex uint32 |
| 33 | + // Queue is the real queue on the physical device. |
| 34 | + Queue NetDevQueueID |
| 35 | + // NetNSID is the network namespace id of the physical device, relative |
| 36 | + // to the caller's namespace. NetNSIDSet reports whether it was provided. |
| 37 | + NetNSID int32 |
| 38 | + NetNSIDSet bool |
| 39 | +} |
| 40 | + |
| 41 | +// NetDevQueue is a queue on a netdevice as reported by queue-get. |
| 42 | +type NetDevQueue struct { |
| 43 | + IfIndex uint32 |
| 44 | + ID uint32 |
| 45 | + Type NetDevQueueType |
| 46 | + NapiID uint32 |
| 47 | + // Lease is non-nil when the queue is bound to a queue on another |
| 48 | + // netdevice via queue leasing. |
| 49 | + Lease *NetDevQueueLease |
| 50 | +} |
| 51 | + |
| 52 | +// netdevRequest builds and executes a request against the "netdev" generic |
| 53 | +// netlink family, returning the attribute lists of each response message. |
| 54 | +func (h *Handle) netdevRequest(command uint8, flags int, attrs []*nl.RtAttr) ([][]syscall.NetlinkRouteAttr, error) { |
| 55 | + f, err := h.GenlFamilyGet(nl.NETDEV_FAMILY_NAME) |
| 56 | + if err != nil { |
| 57 | + return nil, err |
| 58 | + } |
| 59 | + req := h.newNetlinkRequest(int(f.ID), flags) |
| 60 | + req.AddData(&nl.Genlmsg{ |
| 61 | + Command: command, |
| 62 | + Version: nl.NETDEV_FAMILY_VERSION, |
| 63 | + }) |
| 64 | + for _, a := range attrs { |
| 65 | + req.AddData(a) |
| 66 | + } |
| 67 | + |
| 68 | + msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0) |
| 69 | + if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) { |
| 70 | + return nil, executeErr |
| 71 | + } |
| 72 | + out := make([][]syscall.NetlinkRouteAttr, 0, len(msgs)) |
| 73 | + for _, m := range msgs { |
| 74 | + parsed, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:]) |
| 75 | + if err != nil { |
| 76 | + return nil, err |
| 77 | + } |
| 78 | + out = append(out, parsed) |
| 79 | + } |
| 80 | + return out, executeErr |
| 81 | +} |
| 82 | + |
| 83 | +func parseNetDevQueueID(value []byte) (NetDevQueueID, error) { |
| 84 | + var q NetDevQueueID |
| 85 | + attrs, err := nl.ParseRouteAttr(value) |
| 86 | + if err != nil { |
| 87 | + return q, err |
| 88 | + } |
| 89 | + for _, a := range attrs { |
| 90 | + switch a.Attr.Type & nl.NLA_TYPE_MASK { |
| 91 | + case nl.NETDEV_A_QUEUE_ID: |
| 92 | + q.ID = native.Uint32(a.Value) |
| 93 | + case nl.NETDEV_A_QUEUE_TYPE: |
| 94 | + q.Type = NetDevQueueType(native.Uint32(a.Value)) |
| 95 | + } |
| 96 | + } |
| 97 | + return q, nil |
| 98 | +} |
| 99 | + |
| 100 | +func parseNetDevQueueLease(value []byte) (*NetDevQueueLease, error) { |
| 101 | + attrs, err := nl.ParseRouteAttr(value) |
| 102 | + if err != nil { |
| 103 | + return nil, err |
| 104 | + } |
| 105 | + lease := &NetDevQueueLease{} |
| 106 | + for _, a := range attrs { |
| 107 | + switch a.Attr.Type & nl.NLA_TYPE_MASK { |
| 108 | + case nl.NETDEV_A_LEASE_IFINDEX: |
| 109 | + lease.IfIndex = native.Uint32(a.Value) |
| 110 | + case nl.NETDEV_A_LEASE_QUEUE: |
| 111 | + q, err := parseNetDevQueueID(a.Value) |
| 112 | + if err != nil { |
| 113 | + return nil, err |
| 114 | + } |
| 115 | + lease.Queue = q |
| 116 | + case nl.NETDEV_A_LEASE_NETNS_ID: |
| 117 | + lease.NetNSID = int32(native.Uint32(a.Value)) |
| 118 | + lease.NetNSIDSet = true |
| 119 | + } |
| 120 | + } |
| 121 | + return lease, nil |
| 122 | +} |
| 123 | + |
| 124 | +func parseNetDevQueue(attrs []syscall.NetlinkRouteAttr) (*NetDevQueue, error) { |
| 125 | + q := &NetDevQueue{} |
| 126 | + for _, a := range attrs { |
| 127 | + switch a.Attr.Type & nl.NLA_TYPE_MASK { |
| 128 | + case nl.NETDEV_A_QUEUE_IFINDEX: |
| 129 | + q.IfIndex = native.Uint32(a.Value) |
| 130 | + case nl.NETDEV_A_QUEUE_ID: |
| 131 | + q.ID = native.Uint32(a.Value) |
| 132 | + case nl.NETDEV_A_QUEUE_TYPE: |
| 133 | + q.Type = NetDevQueueType(native.Uint32(a.Value)) |
| 134 | + case nl.NETDEV_A_QUEUE_NAPI_ID: |
| 135 | + q.NapiID = native.Uint32(a.Value) |
| 136 | + case nl.NETDEV_A_QUEUE_LEASE: |
| 137 | + lease, err := parseNetDevQueueLease(a.Value) |
| 138 | + if err != nil { |
| 139 | + return nil, err |
| 140 | + } |
| 141 | + q.Lease = lease |
| 142 | + } |
| 143 | + } |
| 144 | + return q, nil |
| 145 | +} |
| 146 | + |
| 147 | +// NetDevQueueGet returns information about a single queue on the netdevice |
| 148 | +// identified by ifIndex, including its lease binding if it has one. |
| 149 | +// Equivalent to: `ynl --do queue-get --json '{"ifindex":.., "id":.., "type":..}'` |
| 150 | +func NetDevQueueGet(ifIndex int, id uint32, qType NetDevQueueType) (*NetDevQueue, error) { |
| 151 | + return pkgHandle.NetDevQueueGet(ifIndex, id, qType) |
| 152 | +} |
| 153 | + |
| 154 | +// NetDevQueueGet returns information about a single queue. See [NetDevQueueGet]. |
| 155 | +func (h *Handle) NetDevQueueGet(ifIndex int, id uint32, qType NetDevQueueType) (*NetDevQueue, error) { |
| 156 | + attrs := []*nl.RtAttr{ |
| 157 | + nl.NewRtAttr(nl.NETDEV_A_QUEUE_IFINDEX, nl.Uint32Attr(uint32(ifIndex))), |
| 158 | + nl.NewRtAttr(nl.NETDEV_A_QUEUE_ID, nl.Uint32Attr(id)), |
| 159 | + nl.NewRtAttr(nl.NETDEV_A_QUEUE_TYPE, nl.Uint32Attr(uint32(qType))), |
| 160 | + } |
| 161 | + msgs, err := h.netdevRequest(nl.NETDEV_CMD_QUEUE_GET, unix.NLM_F_ACK, attrs) |
| 162 | + if err != nil { |
| 163 | + return nil, err |
| 164 | + } |
| 165 | + if len(msgs) == 0 { |
| 166 | + return nil, fmt.Errorf("netlink: no response for queue-get") |
| 167 | + } |
| 168 | + return parseNetDevQueue(msgs[0]) |
| 169 | +} |
| 170 | + |
| 171 | +// NetDevQueueCreateRequest describes a queue-create operation that creates a |
| 172 | +// new rx queue on a virtual netdevice and leases it to a real queue on a |
| 173 | +// physical netdevice. |
| 174 | +type NetDevQueueCreateRequest struct { |
| 175 | + // IfIndex is the virtual netdevice on which to create the new queue. |
| 176 | + IfIndex int |
| 177 | + // Type is the queue type. Only rx queues may be leased today. |
| 178 | + Type NetDevQueueType |
| 179 | + // Lease names the physical device and real queue to lease. |
| 180 | + Lease NetDevQueueLease |
| 181 | +} |
| 182 | + |
| 183 | +// NetDevQueueCreate creates a new queue on a virtual netdevice and leases it to |
| 184 | +// a real queue on a physical netdevice, returning the new queue's id. Requires |
| 185 | +// CAP_NET_ADMIN. |
| 186 | +// Equivalent to: `ynl --do queue-create --json |
| 187 | +// '{"ifindex":.., "type":"rx", "lease":{"ifindex":.., "queue":{"id":.., "type":"rx"}}}'` |
| 188 | +func NetDevQueueCreate(req NetDevQueueCreateRequest) (uint32, error) { |
| 189 | + return pkgHandle.NetDevQueueCreate(req) |
| 190 | +} |
| 191 | + |
| 192 | +// NetDevQueueCreate creates and leases a queue. See [NetDevQueueCreate]. |
| 193 | +func (h *Handle) NetDevQueueCreate(req NetDevQueueCreateRequest) (uint32, error) { |
| 194 | + // Build the nested lease attribute. Container attributes must carry the |
| 195 | + // NLA_F_NESTED flag: the kernel's nla_parse_nested rejects them otherwise |
| 196 | + // ("NLA_F_NESTED is missing"). |
| 197 | + lease := nl.NewRtAttr(unix.NLA_F_NESTED|nl.NETDEV_A_QUEUE_LEASE, nil) |
| 198 | + lease.AddRtAttr(nl.NETDEV_A_LEASE_IFINDEX, nl.Uint32Attr(req.Lease.IfIndex)) |
| 199 | + if req.Lease.NetNSIDSet { |
| 200 | + lease.AddRtAttr(nl.NETDEV_A_LEASE_NETNS_ID, nl.Uint32Attr(uint32(req.Lease.NetNSID))) |
| 201 | + } |
| 202 | + queue := lease.AddRtAttr(unix.NLA_F_NESTED|nl.NETDEV_A_LEASE_QUEUE, nil) |
| 203 | + queue.AddRtAttr(nl.NETDEV_A_QUEUE_ID, nl.Uint32Attr(req.Lease.Queue.ID)) |
| 204 | + queue.AddRtAttr(nl.NETDEV_A_QUEUE_TYPE, nl.Uint32Attr(uint32(req.Lease.Queue.Type))) |
| 205 | + |
| 206 | + attrs := []*nl.RtAttr{ |
| 207 | + nl.NewRtAttr(nl.NETDEV_A_QUEUE_IFINDEX, nl.Uint32Attr(uint32(req.IfIndex))), |
| 208 | + nl.NewRtAttr(nl.NETDEV_A_QUEUE_TYPE, nl.Uint32Attr(uint32(req.Type))), |
| 209 | + lease, |
| 210 | + } |
| 211 | + |
| 212 | + msgs, err := h.netdevRequest(nl.NETDEV_CMD_QUEUE_CREATE, unix.NLM_F_ACK, attrs) |
| 213 | + if err != nil { |
| 214 | + return 0, err |
| 215 | + } |
| 216 | + if len(msgs) == 0 { |
| 217 | + return 0, fmt.Errorf("netlink: no response for queue-create") |
| 218 | + } |
| 219 | + for _, a := range msgs[0] { |
| 220 | + if a.Attr.Type&nl.NLA_TYPE_MASK == nl.NETDEV_A_QUEUE_ID { |
| 221 | + return native.Uint32(a.Value), nil |
| 222 | + } |
| 223 | + } |
| 224 | + return 0, fmt.Errorf("netlink: queue-create reply missing queue id") |
| 225 | +} |
0 commit comments