Commit 2557e2ec authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'netlink-add-nftables-spec-w-multi-messages'

Donald Hunter says:

====================
netlink: Add nftables spec w/ multi messages

This series adds a ynl spec for nftables and extends ynl with a --multi
command line option that makes it possible to send transactional batches
for nftables.

This series includes a patch for nfnetlink which adds ACK processing for
batch begin/end messages. If you'd prefer that to be sent separately to
nf-next then I can do so, but I included it here so that it gets seen in
context.

An example of usage is:

./tools/net/ynl/cli.py \
 --spec Documentation/netlink/specs/nftables.yaml \
 --multi batch-begin '{"res-id": 10}' \
 --multi newtable '{"name": "test", "nfgen-family": 1}' \
 --multi newchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
 --multi batch-end '{"res-id": 10}'
[None, None, None, None]

It can also be used for bundling get requests:

./tools/net/ynl/cli.py \
 --spec Documentation/netlink/specs/nftables.yaml \
 --multi gettable '{"name": "test", "nfgen-family": 1}' \
 --multi getchain '{"name": "chain", "table": "test", "nfgen-family": 1}' \
 --output-json
[{"name": "test", "use": 1, "handle": 1, "flags": [],
 "nfgen-family": 1, "version": 0, "res-id": 2},
 {"table": "test", "name": "chain", "handle": 1, "use": 0,
 "nfgen-family": 1, "version": 0, "res-id": 2}]

There are 2 issues that may be worth resolving:

 - ynl reports errors by raising an NlError exception so only the first
   error gets reported. This could be changed to add errors to the list
   of responses so that multiple errors could be reported.

 - If any message does not get a response (e.g. batch-begin w/o patch 2)
   then ynl waits indefinitely. A recv timeout could be added which
   would allow ynl to terminate.
====================

Link: https://lore.kernel.org/r/20240418104737.77914-1-donald.hunter@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents af046fd1 bf2ac490
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: nftables
protocol: netlink-raw
protonum: 12
doc:
Netfilter nftables configuration over netlink.
definitions:
-
name: nfgenmsg
type: struct
members:
-
name: nfgen-family
type: u8
-
name: version
type: u8
-
name: res-id
byte-order: big-endian
type: u16
-
name: meta-keys
type: enum
entries:
- len
- protocol
- priority
- mark
- iif
- oif
- iifname
- oifname
- iftype
- oiftype
- skuid
- skgid
- nftrace
- rtclassid
- secmark
- nfproto
- l4-proto
- bri-iifname
- bri-oifname
- pkttype
- cpu
- iifgroup
- oifgroup
- cgroup
- prandom
- secpath
- iifkind
- oifkind
- bri-iifpvid
- bri-iifvproto
- time-ns
- time-day
- time-hour
- sdif
- sdifname
- bri-broute
-
name: cmp-ops
type: enum
entries:
- eq
- neq
- lt
- lte
- gt
- gte
-
name: object-type
type: enum
entries:
- unspec
- counter
- quota
- ct-helper
- limit
- connlimit
- tunnel
- ct-timeout
- secmark
- ct-expect
- synproxy
-
name: nat-range-flags
type: flags
entries:
- map-ips
- proto-specified
- proto-random
- persistent
- proto-random-fully
- proto-offset
- netmap
-
name: table-flags
type: flags
entries:
- dormant
- owner
- persist
-
name: chain-flags
type: flags
entries:
- base
- hw-offload
- binding
-
name: set-flags
type: flags
entries:
- anonymous
- constant
- interval
- map
- timeout
- eval
- object
- concat
- expr
attribute-sets:
-
name: empty-attrs
attributes:
-
name: name
type: string
-
name: batch-attrs
attributes:
-
name: genid
type: u32
byte-order: big-endian
-
name: table-attrs
attributes:
-
name: name
type: string
doc: name of the table
-
name: flags
type: u32
byte-order: big-endian
doc: bitmask of flags
enum: table-flags
enum-as-flags: true
-
name: use
type: u32
byte-order: big-endian
doc: number of chains in this table
-
name: handle
type: u64
byte-order: big-endian
doc: numeric handle of the table
-
name: userdata
type: binary
doc: user data
-
name: chain-attrs
attributes:
-
name: table
type: string
doc: name of the table containing the chain
-
name: handle
type: u64
byte-order: big-endian
doc: numeric handle of the chain
-
name: name
type: string
doc: name of the chain
-
name: hook
type: nest
nested-attributes: nft-hook-attrs
doc: hook specification for basechains
-
name: policy
type: u32
byte-order: big-endian
doc: numeric policy of the chain
-
name: use
type: u32
byte-order: big-endian
doc: number of references to this chain
-
name: type
type: string
doc: type name of the chain
-
name: counters
type: nest
nested-attributes: nft-counter-attrs
doc: counter specification of the chain
-
name: flags
type: u32
byte-order: big-endian
doc: chain flags
enum: chain-flags
enum-as-flags: true
-
name: id
type: u32
byte-order: big-endian
doc: uniquely identifies a chain in a transaction
-
name: userdata
type: binary
doc: user data
-
name: counter-attrs
attributes:
-
name: bytes
type: u64
byte-order: big-endian
-
name: packets
type: u64
byte-order: big-endian
-
name: pad
type: pad
-
name: nft-hook-attrs
attributes:
-
name: num
type: u32
byte-order: big-endian
-
name: priority
type: s32
byte-order: big-endian
-
name: dev
type: string
doc: net device name
-
name: devs
type: nest
nested-attributes: hook-dev-attrs
doc: list of net devices
-
name: hook-dev-attrs
attributes:
-
name: name
type: string
multi-attr: true
-
name: nft-counter-attrs
attributes:
-
name: bytes
type: u64
-
name: packets
type: u64
-
name: rule-attrs
attributes:
-
name: table
type: string
doc: name of the table containing the rule
-
name: chain
type: string
doc: name of the chain containing the rule
-
name: handle
type: u64
byte-order: big-endian
doc: numeric handle of the rule
-
name: expressions
type: nest
nested-attributes: expr-list-attrs
doc: list of expressions
-
name: compat
type: nest
nested-attributes: rule-compat-attrs
doc: compatibility specifications of the rule
-
name: position
type: u64
byte-order: big-endian
doc: numeric handle of the previous rule
-
name: userdata
type: binary
doc: user data
-
name: id
type: u32
doc: uniquely identifies a rule in a transaction
-
name: position-id
type: u32
doc: transaction unique identifier of the previous rule
-
name: chain-id
type: u32
doc: add the rule to chain by ID, alternative to chain name
-
name: expr-list-attrs
attributes:
-
name: elem
type: nest
nested-attributes: expr-attrs
multi-attr: true
-
name: expr-attrs
attributes:
-
name: name
type: string
doc: name of the expression type
-
name: data
type: sub-message
sub-message: expr-ops
selector: name
doc: type specific data
-
name: rule-compat-attrs
attributes:
-
name: proto
type: binary
doc: numeric value of the handled protocol
-
name: flags
type: binary
doc: bitmask of flags
-
name: set-attrs
attributes:
-
name: table
type: string
doc: table name
-
name: name
type: string
doc: set name
-
name: flags
type: u32
enum: set-flags
byte-order: big-endian
doc: bitmask of enum nft_set_flags
-
name: key-type
type: u32
byte-order: big-endian
doc: key data type, informational purpose only
-
name: key-len
type: u32
byte-order: big-endian
doc: key data length
-
name: data-type
type: u32
byte-order: big-endian
doc: mapping data type
-
name: data-len
type: u32
byte-order: big-endian
doc: mapping data length
-
name: policy
type: u32
byte-order: big-endian
doc: selection policy
-
name: desc
type: nest
nested-attributes: set-desc-attrs
doc: set description
-
name: id
type: u32
doc: uniquely identifies a set in a transaction
-
name: timeout
type: u64
doc: default timeout value
-
name: gc-interval
type: u32
doc: garbage collection interval
-
name: userdata
type: binary
doc: user data
-
name: pad
type: pad
-
name: obj-type
type: u32
byte-order: big-endian
doc: stateful object type
-
name: handle
type: u64
byte-order: big-endian
doc: set handle
-
name: expr
type: nest
nested-attributes: expr-attrs
doc: set expression
multi-attr: true
-
name: expressions
type: nest
nested-attributes: set-list-attrs
doc: list of expressions
-
name: set-desc-attrs
attributes:
-
name: size
type: u32
byte-order: big-endian
doc: number of elements in set
-
name: concat
type: nest
nested-attributes: set-desc-concat-attrs
doc: description of field concatenation
multi-attr: true
-
name: set-desc-concat-attrs
attributes:
-
name: elem
type: nest
nested-attributes: set-field-attrs
-
name: set-field-attrs
attributes:
-
name: len
type: u32
byte-order: big-endian
-
name: set-list-attrs
attributes:
-
name: elem
type: nest
nested-attributes: expr-attrs
multi-attr: true
-
name: setelem-attrs
attributes:
-
name: key
type: nest
nested-attributes: data-attrs
doc: key value
-
name: data
type: nest
nested-attributes: data-attrs
doc: data value of mapping
-
name: flags
type: binary
doc: bitmask of nft_set_elem_flags
-
name: timeout
type: u64
doc: timeout value
-
name: expiration
type: u64
doc: expiration time
-
name: userdata
type: binary
doc: user data
-
name: expr
type: nest
nested-attributes: expr-attrs
doc: expression
-
name: objref
type: string
doc: stateful object reference
-
name: key-end
type: nest
nested-attributes: data-attrs
doc: closing key value
-
name: expressions
type: nest
nested-attributes: expr-list-attrs
doc: list of expressions
-
name: setelem-list-elem-attrs
attributes:
-
name: elem
type: nest
nested-attributes: setelem-attrs
multi-attr: true
-
name: setelem-list-attrs
attributes:
-
name: table
type: string
-
name: set
type: string
-
name: elements
type: nest
nested-attributes: setelem-list-elem-attrs
-
name: set-id
type: u32
-
name: gen-attrs
attributes:
-
name: id
type: u32
byte-order: big-endian
doc: ruleset generation id
-
name: proc-pid
type: u32
byte-order: big-endian
-
name: proc-name
type: string
-
name: obj-attrs
attributes:
-
name: table
type: string
doc: name of the table containing the expression
-
name: name
type: string
doc: name of this expression type
-
name: type
type: u32
enum: object-type
byte-order: big-endian
doc: stateful object type
-
name: data
type: sub-message
sub-message: obj-data
selector: type
doc: stateful object data
-
name: use
type: u32
byte-order: big-endian
doc: number of references to this expression
-
name: handle
type: u64
byte-order: big-endian
doc: object handle
-
name: pad
type: pad
-
name: userdata
type: binary
doc: user data
-
name: quota-attrs
attributes:
-
name: bytes
type: u64
byte-order: big-endian
-
name: flags # TODO
type: u32
byte-order: big-endian
-
name: pad
type: pad
-
name: consumed
type: u64
byte-order: big-endian
-
name: flowtable-attrs
attributes:
-
name: table
type: string
-
name: name
type: string
-
name: hook
type: nest
nested-attributes: flowtable-hook-attrs
-
name: use
type: u32
byte-order: big-endian
-
name: handle
type: u64
byte-order: big-endian
-
name: pad
type: pad
-
name: flags
type: u32
byte-order: big-endian
-
name: flowtable-hook-attrs
attributes:
-
name: num
type: u32
byte-order: big-endian
-
name: priority
type: u32
byte-order: big-endian
-
name: devs
type: nest
nested-attributes: hook-dev-attrs
-
name: expr-cmp-attrs
attributes:
-
name: sreg
type: u32
byte-order: big-endian
-
name: op
type: u32
byte-order: big-endian
enum: cmp-ops
-
name: data
type: nest
nested-attributes: data-attrs
-
name: data-attrs
attributes:
-
name: value
type: binary
# sub-type: u8
-
name: verdict
type: nest
nested-attributes: verdict-attrs
-
name: verdict-attrs
attributes:
-
name: code
type: u32
byte-order: big-endian
-
name: chain
type: string
-
name: chain-id
type: u32
-
name: expr-counter-attrs
attributes:
-
name: bytes
type: u64
doc: Number of bytes
-
name: packets
type: u64
doc: Number of packets
-
name: pad
type: pad
-
name: expr-flow-offload-attrs
attributes:
-
name: name
type: string
doc: Flow offload table name
-
name: expr-immediate-attrs
attributes:
-
name: dreg
type: u32
byte-order: big-endian
-
name: data
type: nest
nested-attributes: data-attrs
-
name: expr-meta-attrs
attributes:
-
name: dreg
type: u32
byte-order: big-endian
-
name: key
type: u32
byte-order: big-endian
enum: meta-keys
-
name: sreg
type: u32
byte-order: big-endian
-
name: expr-nat-attrs
attributes:
-
name: type
type: u32
byte-order: big-endian
-
name: family
type: u32
byte-order: big-endian
-
name: reg-addr-min
type: u32
byte-order: big-endian
-
name: reg-addr-max
type: u32
byte-order: big-endian
-
name: reg-proto-min
type: u32
byte-order: big-endian
-
name: reg-proto-max
type: u32
byte-order: big-endian
-
name: flags
type: u32
byte-order: big-endian
enum: nat-range-flags
enum-as-flags: true
-
name: expr-payload-attrs
attributes:
-
name: dreg
type: u32
byte-order: big-endian
-
name: base
type: u32
byte-order: big-endian
-
name: offset
type: u32
byte-order: big-endian
-
name: len
type: u32
byte-order: big-endian
-
name: sreg
type: u32
byte-order: big-endian
-
name: csum-type
type: u32
byte-order: big-endian
-
name: csum-offset
type: u32
byte-order: big-endian
-
name: csum-flags
type: u32
byte-order: big-endian
-
name: expr-tproxy-attrs
attributes:
-
name: family
type: u32
byte-order: big-endian
-
name: reg-addr
type: u32
byte-order: big-endian
-
name: reg-port
type: u32
byte-order: big-endian
sub-messages:
-
name: expr-ops
formats:
-
value: bitwise # TODO
-
value: cmp
attribute-set: expr-cmp-attrs
-
value: counter
attribute-set: expr-counter-attrs
-
value: ct # TODO
-
value: flow_offload
attribute-set: expr-flow-offload-attrs
-
value: immediate
attribute-set: expr-immediate-attrs
-
value: lookup # TODO
-
value: meta
attribute-set: expr-meta-attrs
-
value: nat
attribute-set: expr-nat-attrs
-
value: payload
attribute-set: expr-payload-attrs
-
value: tproxy
attribute-set: expr-tproxy-attrs
-
name: obj-data
formats:
-
value: counter
attribute-set: counter-attrs
-
value: quota
attribute-set: quota-attrs
operations:
enum-model: directional
list:
-
name: batch-begin
doc: Start a batch of operations
attribute-set: batch-attrs
fixed-header: nfgenmsg
do:
request:
value: 0x10
attributes:
- genid
reply:
value: 0x10
attributes:
- genid
-
name: batch-end
doc: Finish a batch of operations
attribute-set: batch-attrs
fixed-header: nfgenmsg
do:
request:
value: 0x11
attributes:
- genid
-
name: newtable
doc: Create a new table.
attribute-set: table-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa00
attributes:
- name
-
name: gettable
doc: Get / dump tables.
attribute-set: table-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa01
attributes:
- name
reply:
value: 0xa00
attributes:
- name
-
name: deltable
doc: Delete an existing table.
attribute-set: table-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa02
attributes:
- name
-
name: destroytable
doc: Delete an existing table with destroy semantics (ignoring ENOENT errors).
attribute-set: table-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1a
attributes:
- name
-
name: newchain
doc: Create a new chain.
attribute-set: chain-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa03
attributes:
- name
-
name: getchain
doc: Get / dump chains.
attribute-set: chain-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa04
attributes:
- name
reply:
value: 0xa03
attributes:
- name
-
name: delchain
doc: Delete an existing chain.
attribute-set: chain-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa05
attributes:
- name
-
name: destroychain
doc: Delete an existing chain with destroy semantics (ignoring ENOENT errors).
attribute-set: chain-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1b
attributes:
- name
-
name: newrule
doc: Create a new rule.
attribute-set: rule-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa06
attributes:
- name
-
name: getrule
doc: Get / dump rules.
attribute-set: rule-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa07
attributes:
- name
reply:
value: 0xa06
attributes:
- name
-
name: getrule-reset
doc: Get / dump rules and reset stateful expressions.
attribute-set: rule-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa19
attributes:
- name
reply:
value: 0xa06
attributes:
- name
-
name: delrule
doc: Delete an existing rule.
attribute-set: rule-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa08
attributes:
- name
-
name: destroyrule
doc: Delete an existing rule with destroy semantics (ignoring ENOENT errors).
attribute-set: rule-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1c
attributes:
- name
-
name: newset
doc: Create a new set.
attribute-set: set-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa09
attributes:
- name
-
name: getset
doc: Get / dump sets.
attribute-set: set-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa0a
attributes:
- name
reply:
value: 0xa09
attributes:
- name
-
name: delset
doc: Delete an existing set.
attribute-set: set-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa0b
attributes:
- name
-
name: destroyset
doc: Delete an existing set with destroy semantics (ignoring ENOENT errors).
attribute-set: set-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1d
attributes:
- name
-
name: newsetelem
doc: Create a new set element.
attribute-set: setelem-list-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa0c
attributes:
- name
-
name: getsetelem
doc: Get / dump set elements.
attribute-set: setelem-list-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa0d
attributes:
- name
reply:
value: 0xa0c
attributes:
- name
-
name: getsetelem-reset
doc: Get / dump set elements and reset stateful expressions.
attribute-set: setelem-list-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa21
attributes:
- name
reply:
value: 0xa0c
attributes:
- name
-
name: delsetelem
doc: Delete an existing set element.
attribute-set: setelem-list-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa0e
attributes:
- name
-
name: destroysetelem
doc: Delete an existing set element with destroy semantics.
attribute-set: setelem-list-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1e
attributes:
- name
-
name: getgen
doc: Get / dump rule-set generation.
attribute-set: gen-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa10
attributes:
- name
reply:
value: 0xa0f
attributes:
- name
-
name: newobj
doc: Create a new stateful object.
attribute-set: obj-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa12
attributes:
- name
-
name: getobj
doc: Get / dump stateful objects.
attribute-set: obj-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa13
attributes:
- name
reply:
value: 0xa12
attributes:
- name
-
name: delobj
doc: Delete an existing stateful object.
attribute-set: obj-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa14
attributes:
- name
-
name: destroyobj
doc: Delete an existing stateful object with destroy semantics.
attribute-set: obj-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa1f
attributes:
- name
-
name: newflowtable
doc: Create a new flow table.
attribute-set: flowtable-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa16
attributes:
- name
-
name: getflowtable
doc: Get / dump flow tables.
attribute-set: flowtable-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa17
attributes:
- name
reply:
value: 0xa16
attributes:
- name
-
name: delflowtable
doc: Delete an existing flow table.
attribute-set: flowtable-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa18
attributes:
- name
-
name: destroyflowtable
doc: Delete an existing flow table with destroy semantics.
attribute-set: flowtable-attrs
fixed-header: nfgenmsg
do:
request:
value: 0xa20
attributes:
- name
mcast-groups:
list:
-
name: mgmt
...@@ -427,6 +427,9 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -427,6 +427,9 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
nfnl_unlock(subsys_id); nfnl_unlock(subsys_id);
if (nlh->nlmsg_flags & NLM_F_ACK)
nfnl_err_add(&err_list, nlh, 0, &extack);
while (skb->len >= nlmsg_total_size(0)) { while (skb->len >= nlmsg_total_size(0)) {
int msglen, type; int msglen, type;
...@@ -573,6 +576,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -573,6 +576,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
} else if (err) { } else if (err) {
ss->abort(net, oskb, NFNL_ABORT_NONE); ss->abort(net, oskb, NFNL_ABORT_NONE);
netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL); netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
} else if (nlh->nlmsg_flags & NLM_F_ACK) {
nfnl_err_add(&err_list, nlh, 0, &extack);
} }
} else { } else {
enum nfnl_abort_action abort_action; enum nfnl_abort_action abort_action;
......
...@@ -19,13 +19,28 @@ class YnlEncoder(json.JSONEncoder): ...@@ -19,13 +19,28 @@ class YnlEncoder(json.JSONEncoder):
def main(): def main():
parser = argparse.ArgumentParser(description='YNL CLI sample') description = """
YNL CLI utility - a general purpose netlink utility that uses YAML
specs to drive protocol encoding and decoding.
"""
epilog = """
The --multi option can be repeated to include several do operations
in the same netlink payload.
"""
parser = argparse.ArgumentParser(description=description,
epilog=epilog)
parser.add_argument('--spec', dest='spec', type=str, required=True) parser.add_argument('--spec', dest='spec', type=str, required=True)
parser.add_argument('--schema', dest='schema', type=str) parser.add_argument('--schema', dest='schema', type=str)
parser.add_argument('--no-schema', action='store_true') parser.add_argument('--no-schema', action='store_true')
parser.add_argument('--json', dest='json_text', type=str) parser.add_argument('--json', dest='json_text', type=str)
parser.add_argument('--do', dest='do', type=str)
parser.add_argument('--dump', dest='dump', type=str) group = parser.add_mutually_exclusive_group()
group.add_argument('--do', dest='do', metavar='DO-OPERATION', type=str)
group.add_argument('--multi', dest='multi', nargs=2, action='append',
metavar=('DO-OPERATION', 'JSON_TEXT'), type=str)
group.add_argument('--dump', dest='dump', metavar='DUMP-OPERATION', type=str)
parser.add_argument('--sleep', dest='sleep', type=int) parser.add_argument('--sleep', dest='sleep', type=int)
parser.add_argument('--subscribe', dest='ntf', type=str) parser.add_argument('--subscribe', dest='ntf', type=str)
parser.add_argument('--replace', dest='flags', action='append_const', parser.add_argument('--replace', dest='flags', action='append_const',
...@@ -73,6 +88,10 @@ def main(): ...@@ -73,6 +88,10 @@ def main():
if args.dump: if args.dump:
reply = ynl.dump(args.dump, attrs) reply = ynl.dump(args.dump, attrs)
output(reply) output(reply)
if args.multi:
ops = [ (item[0], json.loads(item[1]), args.flags or []) for item in args.multi ]
reply = ynl.do_multi(ops)
output(reply)
except NlError as e: except NlError as e:
print(e) print(e)
exit(1) exit(1)
......
...@@ -386,12 +386,9 @@ class NetlinkProtocol: ...@@ -386,12 +386,9 @@ class NetlinkProtocol:
def _decode(self, nl_msg): def _decode(self, nl_msg):
return nl_msg return nl_msg
def decode(self, ynl, nl_msg): def decode(self, ynl, nl_msg, op):
msg = self._decode(nl_msg) msg = self._decode(nl_msg)
fixed_header_size = 0 fixed_header_size = ynl._struct_size(op.fixed_header)
if ynl:
op = ynl.rsp_by_value[msg.cmd()]
fixed_header_size = ynl._struct_size(op.fixed_header)
msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size) msg.raw_attrs = NlAttrs(msg.raw, fixed_header_size)
return msg return msg
...@@ -797,7 +794,7 @@ class YnlFamily(SpecFamily): ...@@ -797,7 +794,7 @@ class YnlFamily(SpecFamily):
if 'bad-attr-offs' not in extack: if 'bad-attr-offs' not in extack:
return return
msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set)) msg = self.nlproto.decode(self, NlMsg(request, 0, op.attr_set), op)
offset = self.nlproto.msghdr_size() + self._struct_size(op.fixed_header) offset = self.nlproto.msghdr_size() + self._struct_size(op.fixed_header)
path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset, path = self._decode_extack_path(msg.raw_attrs, op.attr_set, offset,
extack['bad-attr-offs']) extack['bad-attr-offs'])
...@@ -922,7 +919,8 @@ class YnlFamily(SpecFamily): ...@@ -922,7 +919,8 @@ class YnlFamily(SpecFamily):
print("Netlink done while checking for ntf!?") print("Netlink done while checking for ntf!?")
continue continue
decoded = self.nlproto.decode(self, nl_msg) op = self.rsp_by_value[nl_msg.cmd()]
decoded = self.nlproto.decode(self, nl_msg, op)
if decoded.cmd() not in self.async_msg_ids: if decoded.cmd() not in self.async_msg_ids:
print("Unexpected msg id done while checking for ntf", decoded) print("Unexpected msg id done while checking for ntf", decoded)
continue continue
...@@ -940,16 +938,11 @@ class YnlFamily(SpecFamily): ...@@ -940,16 +938,11 @@ class YnlFamily(SpecFamily):
return op['do']['request']['attributes'].copy() return op['do']['request']['attributes'].copy()
def _op(self, method, vals, flags=None, dump=False): def _encode_message(self, op, vals, flags, req_seq):
op = self.ops[method]
nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK nl_flags = Netlink.NLM_F_REQUEST | Netlink.NLM_F_ACK
for flag in flags or []: for flag in flags or []:
nl_flags |= flag nl_flags |= flag
if dump:
nl_flags |= Netlink.NLM_F_DUMP
req_seq = random.randint(1024, 65535)
msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq) msg = self.nlproto.message(nl_flags, op.req_value, 1, req_seq)
if op.fixed_header: if op.fixed_header:
msg += self._encode_struct(op.fixed_header, vals) msg += self._encode_struct(op.fixed_header, vals)
...@@ -957,18 +950,36 @@ class YnlFamily(SpecFamily): ...@@ -957,18 +950,36 @@ class YnlFamily(SpecFamily):
for name, value in vals.items(): for name, value in vals.items():
msg += self._add_attr(op.attr_set.name, name, value, search_attrs) msg += self._add_attr(op.attr_set.name, name, value, search_attrs)
msg = _genl_msg_finalize(msg) msg = _genl_msg_finalize(msg)
return msg
self.sock.send(msg, 0) def _ops(self, ops):
reqs_by_seq = {}
req_seq = random.randint(1024, 65535)
payload = b''
for (method, vals, flags) in ops:
op = self.ops[method]
msg = self._encode_message(op, vals, flags, req_seq)
reqs_by_seq[req_seq] = (op, msg, flags)
payload += msg
req_seq += 1
self.sock.send(payload, 0)
done = False done = False
rsp = [] rsp = []
op_rsp = []
while not done: while not done:
reply = self.sock.recv(self._recv_size) reply = self.sock.recv(self._recv_size)
nms = NlMsgs(reply, attr_space=op.attr_set) nms = NlMsgs(reply, attr_space=op.attr_set)
self._recv_dbg_print(reply, nms) self._recv_dbg_print(reply, nms)
for nl_msg in nms: for nl_msg in nms:
if nl_msg.extack: if nl_msg.nl_seq in reqs_by_seq:
self._decode_extack(msg, op, nl_msg.extack) (op, req_msg, req_flags) = reqs_by_seq[nl_msg.nl_seq]
if nl_msg.extack:
self._decode_extack(req_msg, op, nl_msg.extack)
else:
op = self.rsp_by_value[nl_msg.cmd()]
req_flags = []
if nl_msg.error: if nl_msg.error:
raise NlError(nl_msg) raise NlError(nl_msg)
...@@ -976,13 +987,25 @@ class YnlFamily(SpecFamily): ...@@ -976,13 +987,25 @@ class YnlFamily(SpecFamily):
if nl_msg.extack: if nl_msg.extack:
print("Netlink warning:") print("Netlink warning:")
print(nl_msg) print(nl_msg)
done = True
if Netlink.NLM_F_DUMP in req_flags:
rsp.append(op_rsp)
elif not op_rsp:
rsp.append(None)
elif len(op_rsp) == 1:
rsp.append(op_rsp[0])
else:
rsp.append(op_rsp)
op_rsp = []
del reqs_by_seq[nl_msg.nl_seq]
done = len(reqs_by_seq) == 0
break break
decoded = self.nlproto.decode(self, nl_msg) decoded = self.nlproto.decode(self, nl_msg, op)
# Check if this is a reply to our request # Check if this is a reply to our request
if nl_msg.nl_seq != req_seq or decoded.cmd() != op.rsp_value: if nl_msg.nl_seq not in reqs_by_seq or decoded.cmd() != op.rsp_value:
if decoded.cmd() in self.async_msg_ids: if decoded.cmd() in self.async_msg_ids:
self.handle_ntf(decoded) self.handle_ntf(decoded)
continue continue
...@@ -993,18 +1016,23 @@ class YnlFamily(SpecFamily): ...@@ -993,18 +1016,23 @@ class YnlFamily(SpecFamily):
rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name) rsp_msg = self._decode(decoded.raw_attrs, op.attr_set.name)
if op.fixed_header: if op.fixed_header:
rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header)) rsp_msg.update(self._decode_struct(decoded.raw, op.fixed_header))
rsp.append(rsp_msg) op_rsp.append(rsp_msg)
if dump:
return rsp
if not rsp:
return None
if len(rsp) == 1:
return rsp[0]
return rsp return rsp
def _op(self, method, vals, flags=None, dump=False):
req_flags = flags or []
if dump:
req_flags.append(Netlink.NLM_F_DUMP)
ops = [(method, vals, req_flags)]
return self._ops(ops)[0]
def do(self, method, vals, flags=None): def do(self, method, vals, flags=None):
return self._op(method, vals, flags) return self._op(method, vals, flags)
def dump(self, method, vals): def dump(self, method, vals):
return self._op(method, vals, [], dump=True) return self._op(method, vals, dump=True)
def do_multi(self, ops):
return self._ops(ops)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment