Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #include "bpfilter/xlate/nft/nfgroup.h"
7 :
8 : #include <linux/netfilter/nfnetlink.h>
9 : #include <linux/netlink.h>
10 :
11 : #include <errno.h>
12 : #include <limits.h>
13 : #include <netlink/msg.h>
14 : #include <stdbool.h>
15 : #include <stdint.h>
16 : #include <stdlib.h>
17 : #include <string.h>
18 :
19 : #include "bpfilter/xlate/nft/nfmsg.h"
20 : #include "core/helper.h"
21 : #include "core/list.h"
22 : #include "core/response.h"
23 :
24 : struct bf_nfgroup
25 : {
26 : bf_list messages;
27 : };
28 :
29 20 : int bf_nfgroup_new(struct bf_nfgroup **group)
30 : {
31 20 : bf_assert(group);
32 :
33 19 : _cleanup_bf_nfgroup_ struct bf_nfgroup *_group = NULL;
34 :
35 19 : _group = calloc(1, sizeof(*_group));
36 19 : if (!_group)
37 : return -ENOMEM;
38 :
39 18 : bf_list_init(&_group->messages, &(bf_list_ops) {
40 : .free = (bf_list_ops_free)bf_nfmsg_free,
41 : });
42 :
43 18 : *group = TAKE_PTR(_group);
44 :
45 18 : return 0;
46 : }
47 :
48 15 : int bf_nfgroup_new_from_stream(struct bf_nfgroup **group, struct nlmsghdr *nlh,
49 : size_t length)
50 : {
51 15 : bf_assert(group);
52 14 : bf_assert(nlh);
53 13 : bf_assert(length < INT_MAX);
54 : /* nlmsg_ok() takes an int. length should not be larger than INT_MAX, but
55 : * we check anyway to be safe. */
56 :
57 13 : _cleanup_bf_nfgroup_ struct bf_nfgroup *_group = NULL;
58 13 : int len = (int)length;
59 : int r;
60 :
61 13 : r = bf_nfgroup_new(&_group);
62 13 : if (r < 0)
63 : return r;
64 :
65 71 : while (nlmsg_ok(nlh, len)) {
66 58 : _cleanup_bf_nfmsg_ struct bf_nfmsg *msg = NULL;
67 :
68 58 : if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN ||
69 : nlh->nlmsg_type == NFNL_MSG_BATCH_END) {
70 : // Skip batch messages.
71 0 : nlh = nlmsg_next(nlh, &len);
72 : continue;
73 : }
74 :
75 58 : r = bf_nfmsg_new_from_nlmsghdr(&msg, nlh);
76 58 : if (r < 0)
77 : return r;
78 :
79 58 : r = bf_nfgroup_add_message(_group, msg);
80 58 : if (r < 0)
81 : return r;
82 :
83 58 : TAKE_PTR(msg);
84 :
85 58 : nlh = nlmsg_next(nlh, &len);
86 : }
87 :
88 13 : *group = TAKE_PTR(_group);
89 :
90 13 : return 0;
91 : }
92 :
93 64 : void bf_nfgroup_free(struct bf_nfgroup **group)
94 : {
95 64 : bf_assert(group);
96 :
97 63 : if (!*group)
98 : return;
99 :
100 18 : bf_list_clean(&(*group)->messages);
101 18 : free(*group);
102 18 : *group = NULL;
103 : }
104 :
105 31 : const bf_list *bf_nfgroup_messages(const struct bf_nfgroup *group)
106 : {
107 31 : bf_assert(group);
108 :
109 30 : return &group->messages;
110 : }
111 :
112 13 : size_t bf_nfgroup_size(const struct bf_nfgroup *group)
113 : {
114 13 : bf_assert(group);
115 :
116 : size_t size = 0;
117 :
118 124 : bf_list_foreach (&group->messages, msg_node)
119 55 : size += bf_nfmsg_len(bf_list_node_get_data(msg_node));
120 :
121 12 : return size;
122 : }
123 :
124 11 : bool bf_nfgroup_is_empty(const struct bf_nfgroup *group)
125 : {
126 11 : bf_assert(group);
127 :
128 10 : return bf_list_is_empty(&group->messages);
129 : }
130 :
131 80 : int bf_nfgroup_add_message(struct bf_nfgroup *group, struct bf_nfmsg *msg)
132 : {
133 80 : bf_assert(group);
134 79 : bf_assert(msg);
135 :
136 78 : return bf_list_add_tail(&group->messages, msg);
137 : }
138 :
139 21 : int bf_nfgroup_add_new_message(struct bf_nfgroup *group, struct bf_nfmsg **msg,
140 : uint16_t command, uint16_t seqnr)
141 : {
142 21 : bf_assert(group);
143 :
144 20 : _cleanup_bf_nfmsg_ struct bf_nfmsg *_msg = NULL;
145 : int r;
146 :
147 20 : r = bf_nfmsg_new(&_msg, command, seqnr);
148 20 : if (r < 0)
149 : return r;
150 :
151 20 : r = bf_nfgroup_add_message(group, _msg);
152 20 : if (r < 0)
153 : return r;
154 :
155 20 : if (msg)
156 10 : *msg = _msg;
157 :
158 20 : TAKE_PTR(_msg);
159 :
160 20 : return 0;
161 : }
162 :
163 4 : int bf_nfgroup_to_response(const struct bf_nfgroup *group,
164 : struct bf_response **resp)
165 : {
166 4 : bf_assert(group);
167 3 : bf_assert(resp);
168 :
169 2 : _cleanup_bf_response_ struct bf_response *_resp = NULL;
170 2 : _cleanup_bf_nfmsg_ struct bf_nfmsg *done = NULL;
171 2 : size_t size = bf_nfgroup_size(group);
172 2 : bool is_multipart = bf_list_size(&group->messages) != 1;
173 : void *payload;
174 : int r;
175 :
176 2 : if (is_multipart) {
177 2 : r = bf_nfmsg_new_done(&done);
178 2 : if (r < 0)
179 : return r;
180 :
181 2 : size += bf_nfmsg_len(done);
182 : }
183 :
184 2 : r = bf_response_new_raw(&_resp, size);
185 2 : if (r < 0)
186 : return r;
187 :
188 2 : _resp->type = BF_RES_SUCCESS;
189 2 : _resp->data_len = size;
190 2 : payload = _resp->data;
191 :
192 23 : bf_list_foreach (&group->messages, msg_node) {
193 10 : struct bf_nfmsg *msg = bf_list_node_get_data(msg_node);
194 :
195 10 : memcpy(payload, bf_nfmsg_hdr(msg), bf_nfmsg_len(msg));
196 :
197 10 : if (is_multipart)
198 10 : ((struct nlmsghdr *)payload)->nlmsg_flags |= NLM_F_MULTI;
199 :
200 10 : payload += bf_nfmsg_len(msg);
201 : }
202 :
203 2 : if (is_multipart)
204 2 : memcpy(payload, bf_nfmsg_hdr(done), bf_nfmsg_len(done));
205 :
206 2 : *resp = TAKE_PTR(_resp);
207 :
208 2 : return 0;
209 : }
|