]> git.ipfire.org Git - thirdparty/ipset.git/blame - lib/session.c
Fix json output for -name option
[thirdparty/ipset.git] / lib / session.c
CommitLineData
0fdebb3b 1/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@netfilter.org)
8e0608d3 2 *
3f83fda4
JK
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
8e0608d3
JK
5 * published by the Free Software Foundation.
6 */
7#include <assert.h> /* assert */
5a602182 8#include <endian.h> /* htobe64 */
8e0608d3 9#include <errno.h> /* errno */
ad4d8027 10#include <setjmp.h> /* setjmp, longjmp */
8e0608d3
JK
11#include <stdio.h> /* snprintf */
12#include <stdarg.h> /* va_* */
55fdd96e 13#include <stdbool.h> /* bool */
8e0608d3
JK
14#include <stdlib.h> /* free */
15#include <string.h> /* str* */
16#include <unistd.h> /* getpagesize */
17#include <net/ethernet.h> /* ETH_ALEN */
418a3a4f 18#include <net/if.h> /* IFNAMSIZ */
8e0608d3 19
910d9b62 20#include <libipset/compat.h> /* be64toh() */
3fd6b24a 21#include <libipset/debug.h> /* D() */
8e0608d3
JK
22#include <libipset/data.h> /* IPSET_OPT_* */
23#include <libipset/errcode.h> /* ipset_errcode */
24#include <libipset/print.h> /* ipset_print_* */
25#include <libipset/types.h> /* struct ipset_type */
26#include <libipset/transport.h> /* transport */
27#include <libipset/mnl.h> /* default backend */
28#include <libipset/utils.h> /* STREQ */
55fdd96e 29#include <libipset/ipset.h> /* IPSET_ENV_* */
3713072d 30#include <libipset/list_sort.h> /* list_sort */
8e0608d3
JK
31#include <libipset/session.h> /* prototypes */
32
33#define IPSET_NEST_MAX 4
34
3713072d
JK
35/* When we want to sort the entries */
36struct ipset_sorted {
37 struct list_head list;
38 size_t offset; /* Offset in outbuf */
39};
40
41
8e0608d3
JK
42/* The session structure */
43struct ipset_session {
44 const struct ipset_transport *transport;/* Transport protocol */
45 struct ipset_handle *handle; /* Transport handler */
46 struct ipset_data *data; /* Input/output data */
47 /* Command state */
48 enum ipset_cmd cmd; /* Current command */
49 uint32_t lineno; /* Current lineno in restore mode */
31ee222f 50 uint32_t printed_set; /* Printed sets so far */
8e0608d3 51 char saved_setname[IPSET_MAXNAMELEN]; /* Saved setname */
020936c8 52 const struct ipset_type *saved_type; /* Saved type */
8e0608d3
JK
53 struct nlattr *nested[IPSET_NEST_MAX]; /* Pointer to nest levels */
54 uint8_t nestid; /* Current nest level */
b934ebcd 55 uint8_t protocol; /* The protocol used */
8e0608d3
JK
56 bool version_checked; /* Version checked */
57 /* Output buffer */
3713072d
JK
58 char *outbuf; /* Output buffer */
59 size_t outbuflen; /* Output buffer size */
60 size_t pos; /* Printing position in outbuf */
61 struct list_head sorted; /* Sorted entries */
62 struct list_head pool; /* Pool to reuse */
8e0608d3 63 enum ipset_output_mode mode; /* Output mode */
55fdd96e
JK
64 ipset_print_outfn print_outfn; /* Output function to file */
65 void *p; /* Private data for print_outfn */
3713072d 66 bool sort; /* Print sorted hash:* types */
0b08f9f1 67 size_t save_elem_prefix; /* "add setname " */
55fdd96e
JK
68 /* Session IO */
69 bool normal_io, full_io; /* Default/normal/full IO */
70 FILE *istream, *ostream; /* Session input/output stream */
8e0608d3
JK
71 /* Error/warning reporting */
72 char report[IPSET_ERRORBUFLEN]; /* Error/report buffer */
c387170f 73 enum ipset_err_type err_type; /* ERROR/WARNING/NOTICE */
8e0608d3
JK
74 uint8_t envopts; /* Session env opts */
75 /* Kernel message buffer */
76 size_t bufsize;
8dd10256 77 void *buffer;
8e0608d3
JK
78};
79
80/*
81 * Glue functions
82 */
83
84/**
85 * ipset_session_data - return pointer to the data
86 * @session: session structure
87 *
88 * Returns the pointer to the data structure of the session.
89 */
90struct ipset_data *
91ipset_session_data(const struct ipset_session *session)
92{
93 assert(session);
94 return session->data;
95}
96
97/**
98 * ipset_session_handle - return pointer to the handle
99 * @session: session structure
100 *
101 * Returns the pointer to the transport handle structure of the session.
102 */
103struct ipset_handle *
104ipset_session_handle(const struct ipset_session *session)
105{
106 assert(session);
107 return session->handle;
108}
109
020936c8
JK
110/**
111 * ipset_saved_type - return pointer to the saved type
112 * @session: session structure
113 *
114 * Returns the pointer to the saved type from the last ipset_cmd
115 * It is required to decode type-specific error codes in restore mode.
116 */
117const struct ipset_type *
118ipset_saved_type(const struct ipset_session *session)
119{
120 assert(session);
121 return session->saved_type;
122}
123
cac607bd
JK
124/**
125 * ipset_session_lineno - set session lineno
126 * @session: session structure
127 *
128 * Set session lineno to report parser errors correctly.
129 */
130void
131ipset_session_lineno(struct ipset_session *session, uint32_t lineno)
132{
133 assert(session);
134 session->lineno = lineno;
135}
136
8e0608d3 137/**
55fdd96e 138 * ipset_session_printf_private - returns the session private pointer
8e0608d3 139 * @session: session structure
8e0608d3 140 *
55fdd96e
JK
141 * Returns the private pointer in the session structure,
142 * for private/custom print fuctions.
8e0608d3 143 */
55fdd96e
JK
144void *
145ipset_session_printf_private(struct ipset_session *session)
8e0608d3
JK
146{
147 assert(session);
55fdd96e 148 return session->p;
8e0608d3
JK
149}
150
55fdd96e
JK
151/*
152 * Environment options
153 */
154
8e0608d3
JK
155/**
156 * ipset_envopt_test - test environment option
157 * @session: session structure
158 * @opt: environment option
159 *
160 * Test whether the environment option is set in the session.
161 *
162 * Returns true or false.
163 */
164bool
165ipset_envopt_test(struct ipset_session *session, enum ipset_envopt opt)
166{
167 assert(session);
168 return session->envopts & opt;
169}
170
55fdd96e
JK
171/**
172 * ipset_envopt_set - set environment option
173 * @session: session structure
174 * @opt: environment option
175 *
176 * Set an environment option of the session.
177 */
178void
179ipset_envopt_set(struct ipset_session *session, enum ipset_envopt opt)
180{
181 assert(session);
182 session->envopts |= opt;
183}
184
185/**
186 * ipset_envopt_unset - unset environment option
187 * @session: session structure
188 * @opt: environment option
189 *
190 * Unset an environment option of the session.
191 */
192void
193ipset_envopt_unset(struct ipset_session *session, enum ipset_envopt opt)
194{
195 assert(session);
196 session->envopts &= ~opt;
197}
198
8e0608d3
JK
199/**
200 * ipset_session_output - set the session output mode
201 * @session: session structure
202 * @mode: output mode
203 *
204 * Set the output mode for the session.
205 *
206 * Returns 0 on success or a negative error code.
207 */
208int
209ipset_session_output(struct ipset_session *session,
210 enum ipset_output_mode mode)
211{
212 assert(session);
213 session->mode = mode;
214 return 0;
215}
216
217/*
218 * Error and warning reporting
219 */
220
221/**
222 * ipset_session_report - fill the report buffer
223 * @session: session structure
224 * @type: report type
225 * @fmt: message format
226 *
227 * Fill the report buffer with an error or warning message.
228 * Depending on the report type, set the error or warning
229 * message pointer.
230 *
231 * Returns -1.
232 */
3f83fda4 233int __attribute__((format(printf, 3, 4)))
8e0608d3 234ipset_session_report(struct ipset_session *session,
3f83fda4 235 enum ipset_err_type type,
8e0608d3
JK
236 const char *fmt, ...)
237{
238 int len, offset = 0;
239 va_list args;
3f83fda4 240
8e0608d3
JK
241 assert(session);
242 assert(fmt);
243
c387170f
JK
244 /* Suppress warning/notice when more important message is required */
245 if (session->err_type > IPSET_NO_ERROR && session->err_type < type)
246 session->report[0] = '\0';
247
8e0608d3
JK
248 if (session->lineno != 0 && type == IPSET_ERROR) {
249 sprintf(session->report, "Error in line %u: ",
250 session->lineno);
251 }
252 offset = strlen(session->report);
3f83fda4 253
8e0608d3
JK
254 va_start(args, fmt);
255 len = vsnprintf(session->report + offset,
3f83fda4
JK
256 IPSET_ERRORBUFLEN - 1 - offset,
257 fmt, args);
8e0608d3 258 va_end(args);
3f83fda4 259
8e0608d3
JK
260 if (len >= IPSET_ERRORBUFLEN - 1 - offset)
261 session->report[IPSET_ERRORBUFLEN - 1] = '\0';
3fd6b24a
JK
262 if (strlen(session->report) < IPSET_ERRORBUFLEN - 1)
263 strcat(session->report, "\n");
8e0608d3 264
c387170f
JK
265 session->err_type = type;
266 if (type == IPSET_ERROR)
dbd6853f 267 ipset_data_reset(ipset_session_data(session));
c387170f 268
8e0608d3
JK
269 return -1;
270}
271
51660085
JK
272/**
273 * ipset_session_warning_as_error - set warning as error
274 * @session: session structrure
275 *
276 * Returns -1.
277 */
278int
279ipset_session_warning_as_error(struct ipset_session *session)
280{
c387170f 281 session->err_type = IPSET_ERROR;
51660085
JK
282 ipset_data_reset(ipset_session_data(session));
283 return -1;
284}
285
8e0608d3
JK
286/**
287 * ipset_session_reset - reset the report buffer
288 * @session: session structure
289 *
290 * Reset the report buffer, the error and warning pointers.
291 */
292void
293ipset_session_report_reset(struct ipset_session *session)
294{
295 assert(session);
296 session->report[0] = '\0';
c387170f 297 session->err_type = IPSET_NO_ERROR;
8e0608d3
JK
298}
299
300/**
c387170f 301 * ipset_session_report_msg - return the report buffer
8e0608d3
JK
302 * @session: session structure
303 *
c387170f
JK
304 * Return the pointer to the report buffer.
305 * If there is no error message, the buffer is empty.
8e0608d3
JK
306 */
307const char *
c387170f 308ipset_session_report_msg(const struct ipset_session *session)
8e0608d3
JK
309{
310 assert(session);
311
c387170f 312 return session->report;
8e0608d3
JK
313}
314
315/**
c387170f 316 * ipset_session_report_type - return the type of the report
8e0608d3
JK
317 * @session: session structure
318 *
c387170f 319 * Return the type of the message in the report buffer.
8e0608d3 320 */
c387170f
JK
321enum ipset_err_type
322ipset_session_report_type(const struct ipset_session *session)
8e0608d3
JK
323{
324 assert(session);
325
c387170f 326 return session->err_type;
8e0608d3
JK
327}
328
329/*
330 * Receive data from the kernel
331 */
332
333struct ipset_attr_policy {
334 uint16_t type;
335 uint16_t len;
336 enum ipset_opt opt;
337};
338
339/* Attribute policies and mapping to options */
95b9dfb3 340static const struct ipset_attr_policy cmd_attrs[] = {
8e0608d3
JK
341 [IPSET_ATTR_PROTOCOL] = {
342 .type = MNL_TYPE_U8,
343 },
344 [IPSET_ATTR_SETNAME] = {
345 .type = MNL_TYPE_NUL_STRING,
346 .opt = IPSET_SETNAME,
347 .len = IPSET_MAXNAMELEN,
348 },
349 [IPSET_ATTR_TYPENAME] = {
350 .type = MNL_TYPE_NUL_STRING,
351 .opt = IPSET_OPT_TYPENAME,
352 .len = IPSET_MAXNAMELEN,
353 },
354 /* IPSET_ATTR_SETNAME2 is an alias for IPSET_ATTR_TYPENAME */
355 [IPSET_ATTR_REVISION] = {
356 .type = MNL_TYPE_U8,
357 .opt = IPSET_OPT_REVISION,
358 },
359 [IPSET_ATTR_FAMILY] = {
360 .type = MNL_TYPE_U8,
361 .opt = IPSET_OPT_FAMILY,
362 },
3fd6b24a
JK
363 [IPSET_ATTR_FLAGS] = {
364 .type = MNL_TYPE_U32,
365 .opt = IPSET_OPT_FLAGS,
366 },
8e0608d3
JK
367 [IPSET_ATTR_DATA] = {
368 .type = MNL_TYPE_NESTED,
369 },
370 [IPSET_ATTR_ADT] = {
371 .type = MNL_TYPE_NESTED,
372 },
373 [IPSET_ATTR_REVISION_MIN] = {
374 .type = MNL_TYPE_U8,
375 .opt = IPSET_OPT_REVISION_MIN,
376 },
377 /* IPSET_ATTR_PROTOCOL_MIN is an alias for IPSET_ATTR_REVISION_MIN */
378 [IPSET_ATTR_LINENO] = {
379 .type = MNL_TYPE_U32,
380 .opt = IPSET_OPT_LINENO,
381 },
4a1797e2
JK
382 [IPSET_ATTR_INDEX] = {
383 .type = MNL_TYPE_U16,
384 .opt = IPSET_OPT_INDEX,
385 },
8e0608d3
JK
386};
387
95b9dfb3 388static const struct ipset_attr_policy create_attrs[] = {
8e0608d3 389 [IPSET_ATTR_IP] = {
0d32c5c0 390 .type = MNL_TYPE_NESTED,
8e0608d3
JK
391 .opt = IPSET_OPT_IP,
392 },
393 [IPSET_ATTR_IP_TO] = {
0d32c5c0 394 .type = MNL_TYPE_NESTED,
8e0608d3
JK
395 .opt = IPSET_OPT_IP_TO,
396 },
397 [IPSET_ATTR_CIDR] = {
398 .type = MNL_TYPE_U8,
399 .opt = IPSET_OPT_CIDR,
400 },
401 [IPSET_ATTR_PORT] = {
402 .type = MNL_TYPE_U16,
403 .opt = IPSET_OPT_PORT,
404 },
405 [IPSET_ATTR_PORT_TO] = {
406 .type = MNL_TYPE_U16,
407 .opt = IPSET_OPT_PORT_TO,
408 },
409 [IPSET_ATTR_TIMEOUT] = {
410 .type = MNL_TYPE_U32,
411 .opt = IPSET_OPT_TIMEOUT,
412 },
020936c8
JK
413 [IPSET_ATTR_PROTO] = {
414 .type = MNL_TYPE_U8,
415 .opt = IPSET_OPT_PROTO,
416 },
3fd6b24a 417 [IPSET_ATTR_CADT_FLAGS] = {
8e0608d3 418 .type = MNL_TYPE_U32,
3fd6b24a 419 .opt = IPSET_OPT_CADT_FLAGS,
8e0608d3 420 },
280fe2d4 421 [IPSET_ATTR_INITVAL] = {
8e0608d3 422 .type = MNL_TYPE_U32,
280fe2d4 423 .opt = IPSET_OPT_INITVAL,
8e0608d3
JK
424 },
425 [IPSET_ATTR_HASHSIZE] = {
426 .type = MNL_TYPE_U32,
427 .opt = IPSET_OPT_HASHSIZE,
428 },
429 [IPSET_ATTR_MAXELEM] = {
430 .type = MNL_TYPE_U32,
431 .opt = IPSET_OPT_MAXELEM,
432 },
2dfb973c
VD
433 [IPSET_ATTR_MARKMASK] = {
434 .type = MNL_TYPE_U32,
435 .opt = IPSET_OPT_MARKMASK,
436 },
8e0608d3
JK
437 [IPSET_ATTR_NETMASK] = {
438 .type = MNL_TYPE_U8,
439 .opt = IPSET_OPT_NETMASK,
440 },
de340a7f 441 [IPSET_ATTR_BUCKETSIZE] = {
8e0608d3 442 .type = MNL_TYPE_U8,
de340a7f 443 .opt = IPSET_OPT_BUCKETSIZE,
8e0608d3
JK
444 },
445 [IPSET_ATTR_RESIZE] = {
446 .type = MNL_TYPE_U8,
447 .opt = IPSET_OPT_RESIZE,
448 },
449 [IPSET_ATTR_SIZE] = {
450 .type = MNL_TYPE_U32,
451 .opt = IPSET_OPT_SIZE,
452 },
453 [IPSET_ATTR_ELEMENTS] = {
454 .type = MNL_TYPE_U32,
455 .opt = IPSET_OPT_ELEMENTS,
456 },
457 [IPSET_ATTR_REFERENCES] = {
458 .type = MNL_TYPE_U32,
459 .opt = IPSET_OPT_REFERENCES,
460 },
461 [IPSET_ATTR_MEMSIZE] = {
462 .type = MNL_TYPE_U32,
463 .opt = IPSET_OPT_MEMSIZE,
464 },
b50666c0
VP
465 [IPSET_ATTR_BITMASK] = {
466 .type = MNL_TYPE_NESTED,
467 .opt = IPSET_OPT_BITMASK,
468 },
8e0608d3
JK
469};
470
95b9dfb3 471static const struct ipset_attr_policy adt_attrs[] = {
8e0608d3 472 [IPSET_ATTR_IP] = {
0d32c5c0 473 .type = MNL_TYPE_NESTED,
8e0608d3
JK
474 .opt = IPSET_OPT_IP,
475 },
476 [IPSET_ATTR_IP_TO] = {
0d32c5c0 477 .type = MNL_TYPE_NESTED,
8e0608d3
JK
478 .opt = IPSET_OPT_IP_TO,
479 },
480 [IPSET_ATTR_CIDR] = {
481 .type = MNL_TYPE_U8,
482 .opt = IPSET_OPT_CIDR,
483 },
14ea38fc
VD
484 [IPSET_ATTR_MARK] = {
485 .type = MNL_TYPE_U32,
486 .opt = IPSET_OPT_MARK,
487 },
8e0608d3
JK
488 [IPSET_ATTR_PORT] = {
489 .type = MNL_TYPE_U16,
490 .opt = IPSET_OPT_PORT,
491 },
492 [IPSET_ATTR_PORT_TO] = {
493 .type = MNL_TYPE_U16,
494 .opt = IPSET_OPT_PORT_TO,
495 },
020936c8
JK
496 [IPSET_ATTR_PROTO] = {
497 .type = MNL_TYPE_U8,
498 .opt = IPSET_OPT_PROTO,
499 },
8e0608d3
JK
500 [IPSET_ATTR_TIMEOUT] = {
501 .type = MNL_TYPE_U32,
502 .opt = IPSET_OPT_TIMEOUT,
503 },
3fd6b24a 504 [IPSET_ATTR_CADT_FLAGS] = {
8e0608d3 505 .type = MNL_TYPE_U32,
3fd6b24a 506 .opt = IPSET_OPT_CADT_FLAGS,
8e0608d3
JK
507 },
508 [IPSET_ATTR_LINENO] = {
509 .type = MNL_TYPE_U32,
510 .opt = IPSET_OPT_LINENO,
511 },
512 [IPSET_ATTR_ETHER] = {
513 .type = MNL_TYPE_BINARY,
514 .opt = IPSET_OPT_ETHER,
515 .len = ETH_ALEN,
516 },
517 [IPSET_ATTR_NAME] = {
518 .type = MNL_TYPE_NUL_STRING,
519 .opt = IPSET_OPT_NAME,
520 .len = IPSET_MAXNAMELEN,
521 },
522 [IPSET_ATTR_NAMEREF] = {
523 .type = MNL_TYPE_NUL_STRING,
524 .opt = IPSET_OPT_NAMEREF,
525 .len = IPSET_MAXNAMELEN,
526 },
527 [IPSET_ATTR_IP2] = {
0d32c5c0 528 .type = MNL_TYPE_NESTED,
8e0608d3
JK
529 .opt = IPSET_OPT_IP2,
530 },
531 [IPSET_ATTR_CIDR2] = {
532 .type = MNL_TYPE_U8,
533 .opt = IPSET_OPT_CIDR2,
534 },
bb4f6b81
JK
535 [IPSET_ATTR_IP2_TO] = {
536 .type = MNL_TYPE_NESTED,
537 .opt = IPSET_OPT_IP2_TO,
538 },
418a3a4f
JK
539 [IPSET_ATTR_IFACE] = {
540 .type = MNL_TYPE_NUL_STRING,
541 .opt = IPSET_OPT_IFACE,
542 .len = IFNAMSIZ,
543 },
5a602182
JK
544 [IPSET_ATTR_PACKETS] = {
545 .type = MNL_TYPE_U64,
546 .opt = IPSET_OPT_PACKETS,
547 },
548 [IPSET_ATTR_BYTES] = {
549 .type = MNL_TYPE_U64,
550 .opt = IPSET_OPT_BYTES,
551 },
e1cc3d78
OS
552 [IPSET_ATTR_COMMENT] = {
553 .type = MNL_TYPE_NUL_STRING,
554 .opt = IPSET_OPT_ADT_COMMENT,
555 .len = IPSET_MAX_COMMENT_SIZE + 1,
556 },
c1dd8442
AD
557 [IPSET_ATTR_SKBMARK] = {
558 .type = MNL_TYPE_U64,
559 .opt = IPSET_OPT_SKBMARK,
560 },
561 [IPSET_ATTR_SKBPRIO] = {
562 .type = MNL_TYPE_U32,
563 .opt = IPSET_OPT_SKBPRIO,
564 },
565 [IPSET_ATTR_SKBQUEUE] = {
566 .type = MNL_TYPE_U16,
567 .opt = IPSET_OPT_SKBQUEUE,
568 },
6728c0b2
JK
569 [IPSET_ATTR_PAD] = {
570 .type = MNL_TYPE_UNSPEC,
571 .len = 0,
572 },
8e0608d3
JK
573};
574
95b9dfb3 575static const struct ipset_attr_policy ipaddr_attrs[] = {
0d32c5c0
JK
576 [IPSET_ATTR_IPADDR_IPV4] = {
577 .type = MNL_TYPE_U32,
578 },
579 [IPSET_ATTR_IPADDR_IPV6] = {
580 .type = MNL_TYPE_BINARY,
581 .len = sizeof(union nf_inet_addr),
582 },
583};
584
eb77f0db
JK
585#ifdef IPSET_DEBUG
586static int debug = 1;
587#endif
588
0d32c5c0
JK
589static int
590generic_data_attr_cb(const struct nlattr *attr, void *data,
591 int attr_max, const struct ipset_attr_policy *policy)
592{
8673a740 593 const struct nlattr **tb = data;
0d32c5c0 594 int type = mnl_attr_get_type(attr);
3f83fda4 595
eb77f0db 596 IF_D(debug, "attr type: %u, len %u", type, attr->nla_len);
0d32c5c0 597 if (mnl_attr_type_valid(attr, attr_max) < 0) {
eb77f0db 598 IF_D(debug, "attr type: %u INVALID", type);
0d32c5c0
JK
599 return MNL_CB_ERROR;
600 }
601 if (mnl_attr_validate(attr, policy[type].type) < 0) {
eb77f0db
JK
602 IF_D(debug, "attr type: %u POLICY, attrlen %u", type,
603 mnl_attr_get_payload_len(attr));
0d32c5c0
JK
604 return MNL_CB_ERROR;
605 }
3f83fda4 606 if (policy[type].type == MNL_TYPE_NUL_STRING &&
e1cc3d78 607 mnl_attr_get_payload_len(attr) > policy[type].len)
3f83fda4 608 return MNL_CB_ERROR;
0d32c5c0
JK
609 tb[type] = attr;
610 return MNL_CB_OK;
611}
612
613static int
614create_attr_cb(const struct nlattr *attr, void *data)
615{
616 return generic_data_attr_cb(attr, data,
617 IPSET_ATTR_CREATE_MAX, create_attrs);
618}
619
620static int
621adt_attr_cb(const struct nlattr *attr, void *data)
622{
623 return generic_data_attr_cb(attr, data,
624 IPSET_ATTR_ADT_MAX, adt_attrs);
625}
626
627static int
628ipaddr_attr_cb(const struct nlattr *attr, void *data)
629{
630 return generic_data_attr_cb(attr, data,
631 IPSET_ATTR_IPADDR_MAX, ipaddr_attrs);
632}
633
8e0608d3
JK
634#define FAILURE(format, args...) \
635 { ipset_err(session, format , ## args); return MNL_CB_ERROR; }
636
637static int
638attr2data(struct ipset_session *session, struct nlattr *nla[],
639 int type, const struct ipset_attr_policy attrs[])
640{
641 struct ipset_data *data = session->data;
642 const struct ipset_attr_policy *attr;
643 const void *d;
5a602182 644 uint64_t v64;
24b35d0b
JK
645 uint32_t v32;
646 uint16_t v16;
3fd6b24a 647 int ret;
8e0608d3
JK
648
649 attr = &attrs[type];
650 d = mnl_attr_get_payload(nla[type]);
651
6728c0b2
JK
652 if (attr->type == MNL_TYPE_UNSPEC)
653 return 0;
0d32c5c0
JK
654 if (attr->type == MNL_TYPE_NESTED && attr->opt) {
655 /* IP addresses */
656 struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
8e0608d3 657 uint8_t family = ipset_data_family(data);
0d32c5c0 658 int atype;
24b35d0b 659 D("IP attr type %u", type);
0d32c5c0
JK
660 if (mnl_attr_parse_nested(nla[type],
661 ipaddr_attr_cb, ipattr) < 0)
662 FAILURE("Broken kernel message, cannot validate "
663 "IP address attribute!");
8e0608d3
JK
664
665 /* Validate by hand */
666 switch (family) {
541e3286 667 case NFPROTO_IPV4:
0d32c5c0
JK
668 atype = IPSET_ATTR_IPADDR_IPV4;
669 if (!ipattr[atype])
670 FAILURE("Broken kernel message: IPv4 address "
671 "expected but not received!");
672 if (ipattr[atype]->nla_len < sizeof(uint32_t))
8e0608d3 673 FAILURE("Broken kernel message: "
0d32c5c0
JK
674 "cannot validate IPv4 "
675 "address attribute!");
8e0608d3 676 break;
541e3286 677 case NFPROTO_IPV6:
0d32c5c0
JK
678 atype = IPSET_ATTR_IPADDR_IPV6;
679 if (!ipattr[atype])
680 FAILURE("Broken kernel message: IPv6 address "
681 "expected but not received!");
682 if (ipattr[atype]->nla_len < sizeof(struct in6_addr))
8e0608d3 683 FAILURE("Broken kernel message: "
0d32c5c0
JK
684 "cannot validate IPv6 "
685 "address attribute!");
8e0608d3
JK
686 break;
687 default:
688 FAILURE("Broken kernel message: "
689 "IP address attribute but "
690 "family is unspecified!");
691 }
0d32c5c0 692 d = mnl_attr_get_payload(ipattr[atype]);
8e0608d3 693 } else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
24b35d0b 694 D("netorder attr type %u", type);
8e0608d3 695 switch (attr->type) {
5a602182 696 case MNL_TYPE_U64: {
05169249
JK
697 uint64_t tmp;
698 /* Ensure data alignment */
699 memcpy(&tmp, d, sizeof(tmp));
700 v64 = be64toh(tmp);
5a602182
JK
701 d = &v64;
702 break;
703 }
8e0608d3 704 case MNL_TYPE_U32: {
24b35d0b
JK
705 v32 = ntohl(*(const uint32_t *)d);
706 d = &v32;
8e0608d3
JK
707 break;
708 }
709 case MNL_TYPE_U16: {
24b35d0b
JK
710 v16 = ntohs(*(const uint16_t *)d);
711 d = &v16;
8e0608d3
JK
712 break;
713 }
714 default:
715 break;
716 }
4c16de1a
SB
717 } else if (attr->type == MNL_TYPE_NUL_STRING) {
718 if (!d || strlen(d) >= attr->len)
719 FAILURE("Broken kernel message: "
720 "string type attribute missing or too long!");
8e0608d3 721 }
97a12ba3 722#ifdef IPSET_DEBUG
24b35d0b
JK
723 else
724 D("hostorder attr type %u", type);
3f83fda4 725 if (type == IPSET_ATTR_TYPENAME)
8dd10256 726 D("nla typename %s", (const char *) d);
97a12ba3 727#endif
3fd6b24a 728 ret = ipset_data_set(data, attr->opt, d);
97a12ba3 729#ifdef IPSET_DEBUG
3f83fda4 730 if (type == IPSET_ATTR_TYPENAME)
3fd6b24a 731 D("nla typename %s",
8dd10256 732 (const char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
3f83fda4 733#endif
3fd6b24a 734 return ret;
8e0608d3
JK
735}
736
737#define ATTR2DATA(session, nla, type, attrs) \
738 if (attr2data(session, nla, type, attrs) < 0) \
739 return MNL_CB_ERROR
740
741static const char cmd2name[][9] = {
742 [IPSET_CMD_NONE] = "NONE",
743 [IPSET_CMD_CREATE] = "CREATE",
744 [IPSET_CMD_DESTROY] = "DESTROY",
745 [IPSET_CMD_FLUSH] = "FLUSH",
746 [IPSET_CMD_RENAME] = "RENAME",
747 [IPSET_CMD_SWAP] = "SWAP",
748 [IPSET_CMD_LIST] = "LIST",
749 [IPSET_CMD_SAVE] = "SAVE",
750 [IPSET_CMD_ADD] = "ADD",
751 [IPSET_CMD_DEL] = "DEL",
752 [IPSET_CMD_TEST] = "TEST",
753 [IPSET_CMD_HEADER] = "HEADER",
754 [IPSET_CMD_TYPE] = "TYPE",
755 [IPSET_CMD_PROTOCOL] = "PROTOCOL",
756};
757
758static inline int
759call_outfn(struct ipset_session *session)
760{
55fdd96e
JK
761 int ret = session->print_outfn(session, session->p,
762 "%s", session->outbuf);
3f83fda4 763
8e0608d3 764 session->outbuf[0] = '\0';
3713072d 765 session->pos = 0;
3f83fda4 766
8e0608d3
JK
767 return ret < 0 ? ret : 0;
768}
769
ad4d8027
JK
770/* Handle printing failures */
771static jmp_buf printf_failure;
772
3713072d
JK
773static void
774realloc_outbuf(struct ipset_session *session)
775{
776 char *buf = realloc(session->outbuf,
777 session->outbuflen + IPSET_OUTBUFLEN);
778 if (!buf) {
779 ipset_err(session,
780 "Could not allocate memory to print sorted!");
781 longjmp(printf_failure, 1);
782 }
783 session->outbuf = buf;
784 session->outbuflen += IPSET_OUTBUFLEN;
785}
786
a875d3fb
JK
787static int
788handle_snprintf_error(struct ipset_session *session,
3713072d 789 int ret, int loop)
8e0608d3 790{
3713072d
JK
791 if (ret < 0 || ret + session->pos >= session->outbuflen) {
792 if (session->sort && !loop) {
793 realloc_outbuf(session);
794 return 1;
795 }
8e0608d3 796 /* Buffer was too small, push it out and retry */
3713072d
JK
797 D("print buffer and try again: outbuflen: %lu, pos %lu, ret: %d",
798 session->outbuflen, session->pos, ret);
a875d3fb 799 if (loop) {
ad4d8027 800 ipset_err(session,
8e0608d3 801 "Internal error at printing, loop detected!");
ad4d8027
JK
802 longjmp(printf_failure, 1);
803 }
8e0608d3 804
3713072d 805 session->outbuf[session->pos] = '\0';
a875d3fb
JK
806 if (call_outfn(session)) {
807 ipset_err(session,
808 "Internal error, could not print output buffer!");
809 longjmp(printf_failure, 1);
810 }
811 return 1;
8e0608d3 812 }
3713072d 813 session->pos += ret;
a875d3fb
JK
814 return 0;
815}
816
817static int __attribute__((format(printf, 2, 3)))
818safe_snprintf(struct ipset_session *session, const char *fmt, ...)
819{
820 va_list args;
3713072d 821 int ret, loop = 0;
a875d3fb
JK
822
823 do {
3713072d
JK
824 D("outbuflen: %lu, pos: %lu, retry %u",
825 session->outbuflen, session->pos, loop);
a875d3fb 826 va_start(args, fmt);
3713072d
JK
827 ret = vsnprintf(session->outbuf + session->pos,
828 session->outbuflen - session->pos,
a875d3fb
JK
829 fmt, args);
830 va_end(args);
3713072d 831 loop = handle_snprintf_error(session, ret, loop);
a875d3fb
JK
832 } while (loop);
833
8e0608d3
JK
834 return ret;
835}
836
837static int
838safe_dprintf(struct ipset_session *session, ipset_printfn fn,
839 enum ipset_opt opt)
840{
3713072d 841 int ret, loop = 0;
8e0608d3 842
a875d3fb 843 do {
3713072d
JK
844 D("outbuflen: %lu, len: %lu, retry %u",
845 session->outbuflen, session->pos, loop);
846 ret = fn(session->outbuf + session->pos,
847 session->outbuflen - session->pos,
a875d3fb 848 session->data, opt, session->envopts);
3713072d 849 loop = handle_snprintf_error(session, ret, loop);
a875d3fb 850 } while (loop);
8e0608d3 851
8e0608d3
JK
852 return ret;
853}
3f83fda4 854
8e0608d3
JK
855static int
856list_adt(struct ipset_session *session, struct nlattr *nla[])
857{
404c4638 858 const struct ipset_data *data = session->data;
8e0608d3
JK
859 const struct ipset_type *type;
860 const struct ipset_arg *arg;
3713072d 861 size_t offset = 0;
3fd6b24a 862 int i, found = 0;
2885607f 863 static char last_setname[IPSET_MAXNAMELEN] = "";
3fd6b24a 864
3f83fda4 865 D("enter");
8e0608d3
JK
866 /* Check and load type, family */
867 if (!ipset_data_test(data, IPSET_OPT_TYPE))
868 type = ipset_type_get(session, IPSET_CMD_ADD);
869 else
870 type = ipset_data_get(data, IPSET_OPT_TYPE);
871
872 if (type == NULL)
873 return MNL_CB_ERROR;
8e0608d3 874
3fd6b24a
JK
875 for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
876 if (nla[i]) {
877 found++;
878 ATTR2DATA(session, nla, i, adt_attrs);
879 }
880 D("attr found %u", found);
881 if (!found)
882 return MNL_CB_OK;
883
3713072d
JK
884 if (session->sort) {
885 if (session->outbuflen <= session->pos + 1)
886 realloc_outbuf(session);
887 session->pos++; /* \0 */
888 offset = session->pos;
889 }
890
8e0608d3
JK
891 switch (session->mode) {
892 case IPSET_LIST_SAVE:
893 safe_snprintf(session, "add %s ", ipset_data_setname(data));
894 break;
895 case IPSET_LIST_XML:
f535c61d 896 safe_snprintf(session, "<member><elem>");
8e0608d3 897 break;
2885607f
TO
898 case IPSET_LIST_JSON:
899 /* print separator if a member for this set was printed before */
900 if (STREQ(ipset_data_setname(data), last_setname))
901 safe_snprintf(session, ",");
902 strcpy(last_setname, ipset_data_setname(data));
903 safe_snprintf(session, "\n {\n \"elem\" : \"");
904 break;
8e0608d3
JK
905 case IPSET_LIST_PLAIN:
906 default:
907 break;
908 }
3f83fda4 909
8e0608d3 910 safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
f535c61d
JK
911 if (session->mode == IPSET_LIST_XML)
912 safe_snprintf(session, "</elem>");
2885607f
TO
913 if (session->mode == IPSET_LIST_JSON)
914 safe_snprintf(session, "\"");
8e0608d3 915
d71dd935
JK
916 for (i = 0; type->cmd[IPSET_ADD].args[i] != IPSET_ARG_NONE; i++) {
917 arg = ipset_keyword(type->cmd[IPSET_ADD].args[i]);
918 D("print arg opt %u (%s) %s", arg->opt, arg->name[0],
5a602182
JK
919 ipset_data_test(data, arg->opt) ? "(yes)" : "(missing)");
920 if (!(arg->print && ipset_data_test(data, arg->opt)))
8e0608d3
JK
921 continue;
922 switch (session->mode) {
923 case IPSET_LIST_SAVE:
924 case IPSET_LIST_PLAIN:
f535c61d
JK
925 if (arg->has_arg == IPSET_NO_ARG) {
926 safe_snprintf(session, " %s", arg->name[0]);
8e0608d3 927 break;
f535c61d
JK
928 }
929 safe_snprintf(session, " %s ", arg->name[0]);
8e0608d3
JK
930 safe_dprintf(session, arg->print, arg->opt);
931 break;
932 case IPSET_LIST_XML:
933 if (arg->has_arg == IPSET_NO_ARG) {
934 safe_snprintf(session,
f535c61d 935 "<%s/>", arg->name[0]);
8e0608d3
JK
936 break;
937 }
f535c61d 938 safe_snprintf(session, "<%s>", arg->name[0]);
8e0608d3 939 safe_dprintf(session, arg->print, arg->opt);
f535c61d 940 safe_snprintf(session, "</%s>", arg->name[0]);
8e0608d3 941 break;
2885607f
TO
942 case IPSET_LIST_JSON:
943 if (arg->has_arg == IPSET_NO_ARG) {
944 safe_snprintf(session,
945 ",\n \"%s\" : true", arg->name[0]);
946 break;
947 }
948 safe_snprintf(session, ",\n \"%s\" : ", arg->name[0]);
949 safe_dprintf(session, arg->print, arg->opt);
950 break;
8e0608d3
JK
951 default:
952 break;
953 }
954 }
3f83fda4 955
8e0608d3
JK
956 if (session->mode == IPSET_LIST_XML)
957 safe_snprintf(session, "</member>\n");
2885607f
TO
958 else if (session->mode == IPSET_LIST_JSON)
959 safe_snprintf(session, "\n }");
8e0608d3
JK
960 else
961 safe_snprintf(session, "\n");
962
3713072d
JK
963 if (session->sort) {
964 struct ipset_sorted *sorted;
965
966 if (!list_empty(&session->pool)) {
967 sorted = list_first_entry(&session->pool,
968 struct ipset_sorted, list);
969 list_del(&sorted->list);
970 } else {
971 sorted = calloc(1, sizeof(struct ipset_sorted));
972 if (!sorted) {
973 ipset_err(session,
974 "Could not allocate memory to print sorted!");
975 longjmp(printf_failure, 1);
976 }
977 }
978 sorted->offset = offset;
979 list_add_tail(&sorted->list, &session->sorted);
980 }
8e0608d3
JK
981 return MNL_CB_OK;
982}
983
3fd6b24a 984#define FAMILY_TO_STR(f) \
541e3286
JE
985 ((f) == NFPROTO_IPV4 ? "inet" : \
986 (f) == NFPROTO_IPV6 ? "inet6" : "any")
3fd6b24a 987
8e0608d3
JK
988static int
989list_create(struct ipset_session *session, struct nlattr *nla[])
990{
404c4638 991 const struct ipset_data *data = session->data;
8e0608d3
JK
992 const struct ipset_type *type;
993 const struct ipset_arg *arg;
994 uint8_t family;
995 int i;
2885607f 996 static bool firstipset = true;
8e0608d3
JK
997
998 for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
999 if (nla[i]) {
1000 D("add attr %u, opt %u", i, create_attrs[i].opt);
1001 ATTR2DATA(session, nla, i, create_attrs);
1002 }
1003
1004 type = ipset_type_check(session);
1005 if (type == NULL)
1006 return MNL_CB_ERROR;
1007 family = ipset_data_family(data);
1008
0b08f9f1 1009 session->save_elem_prefix = strlen(ipset_data_setname(data)) + 5;
8e0608d3
JK
1010 switch (session->mode) {
1011 case IPSET_LIST_SAVE:
f535c61d 1012 safe_snprintf(session, "create %s %s",
8e0608d3
JK
1013 ipset_data_setname(data),
1014 type->name);
8e0608d3
JK
1015 break;
1016 case IPSET_LIST_PLAIN:
31ee222f 1017 safe_snprintf(session, "%sName: %s\n"
f535c61d 1018 "Type: %s\nRevision: %u\nHeader:",
31ee222f 1019 session->printed_set ? "\n" : "",
8e0608d3 1020 ipset_data_setname(data),
d95c2f1e 1021 type->name, type->revision);
8e0608d3
JK
1022 break;
1023 case IPSET_LIST_XML:
1024 safe_snprintf(session,
1025 "<ipset name=\"%s\">\n"
f535c61d
JK
1026 "<type>%s</type>\n"
1027 "<revision>%u</revision>\n"
1028 "<header>",
8e0608d3 1029 ipset_data_setname(data),
d95c2f1e 1030 type->name, type->revision);
8e0608d3 1031 break;
2885607f
TO
1032 case IPSET_LIST_JSON:
1033 if (!firstipset)
1034 safe_snprintf(session, ",\n");
1035 firstipset = false;
1036 safe_snprintf(session,
1037 " \{\n"
1038 " \"name\" : \"%s\",\n"
1039 " \"type\" : \"%s\",\n"
1040 " \"revision\" : %u,\n"
1041 " \"header\" : \{\n",
1042 ipset_data_setname(data),
1043 type->name, type->revision);
1044 break;
8e0608d3
JK
1045 default:
1046 break;
1047 }
1048
d71dd935
JK
1049 D("type %s, rev %u", type->name, type->revision);
1050 for (i = 0; type->cmd[IPSET_CREATE].args[i] != IPSET_ARG_NONE; i++) {
1051 arg = ipset_keyword(type->cmd[IPSET_CREATE].args[i]);
1052 D("create print arg opt %u (%s) %s", arg->opt,
1053 arg->name[0] ? arg->name[0] : "",
1054 ipset_data_test(data, arg->opt) ? "(yes)" : "(missing)");
3f83fda4
JK
1055 if (!arg->print ||
1056 !ipset_data_test(data, arg->opt) ||
1057 (arg->opt == IPSET_OPT_FAMILY &&
1058 family == type->family))
8e0608d3
JK
1059 continue;
1060 switch (session->mode) {
1061 case IPSET_LIST_SAVE:
1062 case IPSET_LIST_PLAIN:
f535c61d
JK
1063 if (arg->has_arg == IPSET_NO_ARG) {
1064 safe_snprintf(session, " %s", arg->name[0]);
8e0608d3 1065 break;
f535c61d
JK
1066 }
1067 safe_snprintf(session, " %s ", arg->name[0]);
8e0608d3 1068 safe_dprintf(session, arg->print, arg->opt);
8e0608d3
JK
1069 break;
1070 case IPSET_LIST_XML:
1071 if (arg->has_arg == IPSET_NO_ARG) {
1072 safe_snprintf(session,
f535c61d 1073 "<%s/>", arg->name[0]);
8e0608d3
JK
1074 break;
1075 }
f535c61d 1076 safe_snprintf(session, "<%s>", arg->name[0]);
8e0608d3 1077 safe_dprintf(session, arg->print, arg->opt);
f535c61d 1078 safe_snprintf(session, "</%s>", arg->name[0]);
8e0608d3 1079 break;
2885607f
TO
1080 case IPSET_LIST_JSON:
1081 if (arg->has_arg == IPSET_NO_ARG) {
1082 safe_snprintf(session,
1083 " \"%s\" : true,\n", arg->name[0]);
1084 break;
1085 }
1086 if (arg->opt == IPSET_OPT_FAMILY) {
1087 safe_snprintf(session, " \"%s\" : \"", arg->name[0]);
1088 safe_dprintf(session, arg->print, arg->opt);
1089 safe_snprintf(session, "\",\n", arg->name[0]);
1090 break;
1091 }
1092 safe_snprintf(session, " \"%s\" : ", arg->name[0]);
1093 safe_dprintf(session, arg->print, arg->opt);
1094 safe_snprintf(session, ",\n", arg->name[0]);
1095 break;
8e0608d3
JK
1096 default:
1097 break;
1098 }
1099 }
1100 switch (session->mode) {
1101 case IPSET_LIST_SAVE:
1102 safe_snprintf(session, "\n");
1103 break;
1104 case IPSET_LIST_PLAIN:
8e0608d3
JK
1105 safe_snprintf(session, "\nSize in memory: ");
1106 safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
1107 safe_snprintf(session, "\nReferences: ");
1108 safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
0c7e18ed 1109 if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
eb9af8fe
EM
1110 safe_snprintf(session, "\nNumber of entries: ");
1111 safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
1112 }
bf8463fb
JK
1113 safe_snprintf(session,
1114 session->envopts & IPSET_ENV_LIST_HEADER ?
1115 "\n" : "\nMembers:\n");
8e0608d3
JK
1116 break;
1117 case IPSET_LIST_XML:
f535c61d 1118 safe_snprintf(session, "\n<memsize>");
8e0608d3 1119 safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
f535c61d 1120 safe_snprintf(session, "</memsize>\n<references>");
8e0608d3 1121 safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
eb9af8fe 1122 safe_snprintf(session, "</references>\n");
0c7e18ed 1123 if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
eb9af8fe
EM
1124 safe_snprintf(session, "<numentries>");
1125 safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
1126 safe_snprintf(session, "</numentries>\n");
1127 }
31ee222f
JK
1128 safe_snprintf(session,
1129 session->envopts & IPSET_ENV_LIST_HEADER ?
eb9af8fe
EM
1130 "</header>\n" :
1131 "</header>\n<members>\n");
8e0608d3 1132 break;
2885607f
TO
1133 case IPSET_LIST_JSON:
1134 safe_snprintf(session, " \"memsize\" : ");
1135 safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
1136 safe_snprintf(session, ",\n \"references\" : ");
1137 safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
1138 if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
1139 safe_snprintf(session, ",\n \"numentries\" : ");
1140 safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
1141 }
1142 safe_snprintf(session, "\n");
1143 safe_snprintf(session,
1144 session->envopts & IPSET_ENV_LIST_HEADER ?
1145 " },\n" :
1146 " },\n \"members\" : [");
1147 break;
8e0608d3
JK
1148 default:
1149 break;
1150 }
31ee222f 1151 session->printed_set++;
8e0608d3 1152
3713072d
JK
1153 session->sort = strncmp(type->name, "hash:", 5) == 0 &&
1154 ipset_envopt_test(session, IPSET_ENV_SORTED);
1155
8e0608d3
JK
1156 return MNL_CB_OK;
1157}
1158
0b08f9f1
JK
1159/* "<member><elem>" */
1160#define XML_ELEM_PREFIX_LEN 14
1161
1162/* Core should handle sorting more directly */
3713072d
JK
1163static int
1164bystrcmp(void *priv, struct list_head *a, struct list_head *b)
1165{
1166 struct ipset_session *session = priv;
1167 struct ipset_sorted *x = list_entry(a, struct ipset_sorted, list);
1168 struct ipset_sorted *y = list_entry(b, struct ipset_sorted, list);
0b08f9f1
JK
1169 char *sep1, *x1 = session->outbuf + x->offset;
1170 char *sep2, *x2 = session->outbuf + y->offset;
1171 long int n1, n2;
1172 int neg = 1;
1173
1174 if (session->envopts & IPSET_ENV_RESOLVE)
1175 return strcmp(x1, x2);
1176
1177 /* Skip prefix */
1178 switch (session->mode) {
1179 case IPSET_LIST_SAVE:
1180 /* "add setname " */
1181 x1 += session->save_elem_prefix;
1182 x2 += session->save_elem_prefix;
1183 break;
1184 case IPSET_LIST_XML:
1185 /* "<member><elem>" */
1186 x1 += XML_ELEM_PREFIX_LEN;
1187 x2 += XML_ELEM_PREFIX_LEN;
1188 break;
1189 default:
1190 break;
1191 }
3713072d 1192
0b08f9f1
JK
1193 while (*x1 != '\0' && *x2 != '\0') {
1194 n1 = strtol(x1, &sep1, 16);
1195 n2 = strtol(x2, &sep2, 16);
1196 if (x1 == sep1 || x2 == sep2) {
1197 /* No leading numbers: proto:port, iface, setname */
1198 n1 = strcspn(x1, ":,");
1199 n2 = strcspn(x2, ":,");
1200 if (n1 != 0 || n2 != 0) {
1201 if (n1 == n2) {
1202 n2 = strncmp(x1, x2, n1);
1203 if (n2 == 0) {
1204 x1 += n1 + 1;
1205 x2 += n1 + 1;
1206 neg = 1;
1207 continue;
1208 }
1209 return n2;
1210 }
1211 return n1 < n2 ? -1 : 1;
1212 }
1213 /* Setname or iface */
1214 return strcmp(x1, x2);
1215 }
1216 /* Leading numbers found */
1217 if (n1 != n2)
1218 return neg * (n1 - n2);
1219 /* Check subnet separator */
1220 if (*sep1 == '/' || *sep2 == '/') {
1221 if (*sep1 == *sep2)
1222 neg = *sep1 == '/' ? -1 : 1;
1223 else if (*sep1 == '/')
1224 return 1;
1225 else
1226 return -1;
1227 }
1228 x1 = ++sep1;
1229 x2 = ++sep2;
1230 /* Handle IPv6 '::' case */
1231 if (*x1 == ':' || *x2 == ':') {
1232 if (*x1 == *x2) {
1233 x1++;
1234 x2++;
1235 } else if (*x1 == ':')
1236 return -1;
1237 else
1238 return 1;
1239 }
1240 }
1241 return *x1 != '\0' ? 1 : (*x2 != '\0' ? -1 : 0);
3713072d
JK
1242}
1243
8e0608d3 1244static int
f535c61d 1245print_set_done(struct ipset_session *session, bool callback_done)
8e0608d3 1246{
3fd6b24a
JK
1247 D("called for %s", session->saved_setname[0] == '\0'
1248 ? "NONE" : session->saved_setname);
3713072d
JK
1249 if (session->sort) {
1250 struct ipset_sorted *pos;
1251 int ret;
1252
1253 /* Print set header */
1254 ret = call_outfn(session);
1255
1256 if (ret)
1257 return MNL_CB_ERROR;
1258
1259 list_sort(session, &session->sorted, bystrcmp);
1260
1261 list_for_each_entry(pos, &session->sorted, list) {
1262 ret = session->print_outfn(session, session->p,
1263 "%s",
1264 session->outbuf + pos->offset);
1265 if (ret < 0)
1266 return MNL_CB_ERROR;
1267 }
1268 list_splice(&session->sorted, &session->pool);
1269 INIT_LIST_HEAD(&session->sorted);
1270 }
8e0608d3
JK
1271 switch (session->mode) {
1272 case IPSET_LIST_XML:
31ee222f
JK
1273 if (session->envopts & IPSET_ENV_LIST_SETNAME)
1274 break;
1275 if (session->envopts & IPSET_ENV_LIST_HEADER) {
1276 if (session->saved_setname[0] != '\0')
1277 safe_snprintf(session, "</ipset>\n");
1278 break;
1279 }
1280 if (session->saved_setname[0] != '\0')
f535c61d 1281 safe_snprintf(session, "</members>\n</ipset>\n");
8e0608d3 1282 break;
2885607f
TO
1283 case IPSET_LIST_JSON:
1284 if (session->envopts & IPSET_ENV_LIST_SETNAME)
1285 break;
1286 if (session->envopts & IPSET_ENV_LIST_HEADER) {
1287 if (session->saved_setname[0] != '\0')
1288 safe_snprintf(session, " }");
1289 break;
1290 }
1291 if (session->saved_setname[0] != '\0')
1292 safe_snprintf(session, "\n ]\n }");
1293 break;
8e0608d3 1294 default:
8e0608d3
JK
1295 break;
1296 }
f535c61d
JK
1297 if (callback_done && session->mode == IPSET_LIST_XML)
1298 safe_snprintf(session, "</ipsets>\n");
2885607f
TO
1299 if (callback_done && session->mode == IPSET_LIST_JSON)
1300 safe_snprintf(session, "\n]\n");
8e0608d3
JK
1301 return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
1302}
1303
8e0608d3
JK
1304static int
1305callback_list(struct ipset_session *session, struct nlattr *nla[],
1306 enum ipset_cmd cmd)
1307{
1308 struct ipset_data *data = session->data;
8b3824f9 1309 static bool firstipset = true;
8e0608d3 1310
31ee222f
JK
1311 if (setjmp(printf_failure)) {
1312 session->saved_setname[0] = '\0';
1313 session->printed_set = 0;
ad4d8027 1314 return MNL_CB_ERROR;
31ee222f 1315 }
ad4d8027 1316
8e0608d3
JK
1317 if (!nla[IPSET_ATTR_SETNAME])
1318 FAILURE("Broken %s kernel message: missing setname!",
1319 cmd2name[cmd]);
1320
1321 ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
3fd6b24a 1322 D("setname %s", ipset_data_setname(data));
89644719
JK
1323 if (session->envopts & IPSET_ENV_LIST_SETNAME &&
1324 session->mode != IPSET_LIST_SAVE) {
bf8463fb
JK
1325 if (session->mode == IPSET_LIST_XML)
1326 safe_snprintf(session, "<ipset name=\"%s\"/>\n",
1327 ipset_data_setname(data));
8b3824f9
M
1328 else if (session->mode == IPSET_LIST_JSON) {
1329 if (!firstipset)
1330 safe_snprintf(session, ",\n");
1331 firstipset = false;
1332 safe_snprintf(session, " { \"name\" : \"%s\" }",
2885607f 1333 ipset_data_setname(data));
8b3824f9 1334 } else
bf8463fb
JK
1335 safe_snprintf(session, "%s\n",
1336 ipset_data_setname(data));
1337 return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
1338 }
1339
8e0608d3
JK
1340 if (STREQ(ipset_data_setname(data), session->saved_setname)) {
1341 /* Header part already seen */
3f83fda4
JK
1342 if (ipset_data_test(data, IPSET_OPT_TYPE) &&
1343 nla[IPSET_ATTR_DATA] != NULL)
8e0608d3
JK
1344 FAILURE("Broken %s kernel message: "
1345 "extra DATA received!", cmd2name[cmd]);
1346 } else {
1347 if (nla[IPSET_ATTR_DATA] == NULL)
1348 FAILURE("Broken %s kernel message: "
1349 "missing DATA part!", cmd2name[cmd]);
3f83fda4 1350
8e0608d3
JK
1351 /* Close previous set printing */
1352 if (session->saved_setname[0] != '\0')
f535c61d 1353 print_set_done(session, false);
8e0608d3
JK
1354 }
1355
1356 if (nla[IPSET_ATTR_DATA] != NULL) {
1357 struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
1358
3f83fda4
JK
1359 if (!(nla[IPSET_ATTR_TYPENAME] &&
1360 nla[IPSET_ATTR_FAMILY] &&
1361 nla[IPSET_ATTR_REVISION]))
8e0608d3
JK
1362 FAILURE("Broken %s kernel message: missing %s!",
1363 cmd2name[cmd],
3f83fda4
JK
1364 !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1365 !nla[IPSET_ATTR_FAMILY] ? "family" :
1366 "revision");
8e0608d3 1367
3fd6b24a
JK
1368 /* Reset CREATE specific flags */
1369 ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
1370 D("nla typename %s",
1371 (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME]));
81242183 1372
8e0608d3
JK
1373 ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
1374 ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1375 ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
3fd6b24a
JK
1376 D("head: family %u, typename %s",
1377 ipset_data_family(data),
8dd10256 1378 (const char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
8e0608d3
JK
1379 if (mnl_attr_parse_nested(nla[IPSET_ATTR_DATA],
1380 create_attr_cb, cattr) < 0)
1381 FAILURE("Broken %s kernel message: "
1382 "cannot validate DATA attributes!",
1383 cmd2name[cmd]);
1384 if (list_create(session, cattr) != MNL_CB_OK)
1385 return MNL_CB_ERROR;
1386 strcpy(session->saved_setname, ipset_data_setname(data));
1387 }
3f83fda4 1388
8e0608d3
JK
1389 if (nla[IPSET_ATTR_ADT] != NULL) {
1390 struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1];
3fd6b24a 1391
8e0608d3 1392 mnl_attr_for_each_nested(tb, nla[IPSET_ATTR_ADT]) {
3fd6b24a 1393 D("ADT attributes for %s", ipset_data_setname(data));
8e0608d3
JK
1394 memset(adt, 0, sizeof(adt));
1395 /* Reset ADT specific flags */
1396 ipset_data_flags_unset(data, IPSET_ADT_FLAGS);
1397 if (mnl_attr_parse_nested(tb, adt_attr_cb, adt) < 0)
1398 FAILURE("Broken %s kernel message: "
1399 "cannot validate ADT attributes!",
1400 cmd2name[cmd]);
1401 if (list_adt(session, adt) != MNL_CB_OK)
1402 return MNL_CB_ERROR;
1403 }
3713072d
JK
1404 if (session->sort)
1405 return MNL_CB_OK;
8e0608d3
JK
1406 }
1407 return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
1408}
1409
1410#ifndef IPSET_PROTOCOL_MIN
1411#define IPSET_PROTOCOL_MIN IPSET_PROTOCOL
1412#endif
1413
1414#ifndef IPSET_PROTOCOL_MAX
1415#define IPSET_PROTOCOL_MAX IPSET_PROTOCOL
1416#endif
1417
1418static int
1419callback_version(struct ipset_session *session, struct nlattr *nla[])
1420{
1421 uint8_t min, max;
3f83fda4 1422
8e0608d3
JK
1423 min = max = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
1424
1425 if (nla[IPSET_ATTR_PROTOCOL_MIN]) {
1426 min = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL_MIN]);
1427 D("min: %u", min);
1428 }
1429
1430 if (min > IPSET_PROTOCOL_MAX || max < IPSET_PROTOCOL_MIN)
1431 FAILURE("Cannot communicate with kernel: "
1432 "Kernel support protocol versions %u-%u "
1433 "while userspace supports protocol versions %u-%u",
1434 min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
1435
3f83fda4
JK
1436 if (!(session->envopts & IPSET_ENV_QUIET) &&
1437 max != IPSET_PROTOCOL_MAX)
8e0608d3
JK
1438 ipset_warn(session,
1439 "Kernel support protocol versions %u-%u "
1440 "while userspace supports protocol versions %u-%u",
1441 min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
1442
b934ebcd 1443 session->protocol = MIN(max, IPSET_PROTOCOL_MAX);
8e0608d3
JK
1444 session->version_checked = true;
1445
1446 return MNL_CB_STOP;
1447}
1448
1449static int
1450callback_header(struct ipset_session *session, struct nlattr *nla[])
1451{
1452 const char *setname;
404c4638 1453 const struct ipset_data *data = session->data;
3f83fda4 1454
8e0608d3
JK
1455 if (!nla[IPSET_ATTR_SETNAME])
1456 FAILURE("Broken HEADER kernel message: missing setname!");
1457
1458 setname = mnl_attr_get_str(nla[IPSET_ATTR_SETNAME]);
1459 if (!STREQ(setname, ipset_data_setname(data)))
1460 FAILURE("Broken HEADER kernel message: sent setname `%s' "
1461 "does not match with received one `%s'!",
1462 ipset_data_setname(data), setname);
3f83fda4
JK
1463
1464 if (!(nla[IPSET_ATTR_TYPENAME] &&
1465 nla[IPSET_ATTR_REVISION] &&
1466 nla[IPSET_ATTR_FAMILY]))
8e0608d3
JK
1467 FAILURE("Broken HEADER kernel message: "
1468 "missing attribute '%s'!",
1469 !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1470 !nla[IPSET_ATTR_REVISION] ? "revision" :
1471 "family");
1472
1473 ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1474 ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
1475 ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
3fd6b24a 1476 D("got family: %u", ipset_data_family(session->data));
8e0608d3
JK
1477
1478 return MNL_CB_STOP;
1479}
1480
1481static int
1482callback_type(struct ipset_session *session, struct nlattr *nla[])
1483{
404c4638 1484 const struct ipset_data *data = session->data;
8e0608d3 1485 const char *typename, *orig;
3f83fda4
JK
1486
1487 if (!(nla[IPSET_ATTR_TYPENAME] &&
1488 nla[IPSET_ATTR_REVISION] &&
1489 nla[IPSET_ATTR_FAMILY]))
8e0608d3
JK
1490 FAILURE("Broken TYPE kernel message: "
1491 "missing attribute '%s'!",
1492 !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1493 !nla[IPSET_ATTR_REVISION] ? "revision" :
1494 "family");
1495
1496 typename = mnl_attr_get_str(nla[IPSET_ATTR_TYPENAME]);
1497 orig = ipset_data_get(data, IPSET_OPT_TYPENAME);
1498 if (!STREQ(typename, orig))
1499 FAILURE("Broken TYPE kernel message: sent typename `%s' "
1500 "does not match with received one `%s'!",
1501 orig, typename);
3f83fda4 1502
8e0608d3
JK
1503 ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1504 ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
1505 ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
1506 if (nla[IPSET_ATTR_REVISION_MIN])
1507 ATTR2DATA(session, nla, IPSET_ATTR_REVISION_MIN, cmd_attrs);
1508
1509 return MNL_CB_STOP;
1510}
1511
1512static int
1513cmd_attr_cb(const struct nlattr *attr, void *data)
1514{
1515 return generic_data_attr_cb(attr, data, IPSET_ATTR_CMD_MAX, cmd_attrs);
1516}
1517
1518#if 0
1519static int
1520mnl_attr_parse_dbg(const struct nlmsghdr *nlh, int offset,
1521 mnl_attr_cb_t cb, void *data)
1522{
1523 int ret = MNL_CB_OK;
1524 struct nlattr *attr = mnl_nlmsg_get_payload_offset(nlh, offset);
1525 int len = nlh->nlmsg_len - MNL_NLMSG_HDRLEN - MNL_ALIGN(offset);
3f83fda4 1526
8e0608d3
JK
1527 while (mnl_attr_ok(attr, len)) {
1528 D("attr: type %u, attrlen %u, len %u",
1529 mnl_attr_get_type(attr), attr->nla_len, len);
1530 if (cb && (ret = cb(attr, data)) <= MNL_CB_STOP)
1531 return ret;
1532 attr = mnl_attr_next(attr, &len);
1533 }
1534 return ret;
1535}
1536#endif
1537
1538static int
1539callback_data(const struct nlmsghdr *nlh, void *data)
1540{
1541 struct ipset_session *session = data;
1542 struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
1543 uint8_t proto, cmd;
1544 int ret = MNL_CB_OK, nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
3f83fda4 1545
8e0608d3
JK
1546 D("called, nlmsg_len %u", nlh->nlmsg_len);
1547 cmd = ipset_get_nlmsg_type(nlh);
1548 if (cmd == IPSET_CMD_LIST && session->cmd == IPSET_CMD_SAVE)
1549 /* Kernel always send IPSET_CMD_LIST */
1550 cmd = IPSET_CMD_SAVE;
1551
1552 if (cmd != session->cmd)
1553 FAILURE("Protocol error, we sent command %s "
1554 "and received %s[%u]",
1555 cmd2name[session->cmd],
1556 cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
1557
1558 if (mnl_attr_parse(nlh, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
1559 FAILURE("Broken %s kernel message: "
1560 "cannot validate and parse attributes",
1561 cmd2name[cmd]);
1562
1563 if (!nla[IPSET_ATTR_PROTOCOL])
1564 FAILURE("Sad, sad day: kernel message %s "
3f83fda4
JK
1565 "does not carry the protocol version.",
1566 cmd2name[cmd]);
8e0608d3
JK
1567
1568 proto = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
1569
1570 /* Check protocol */
b934ebcd 1571 if (cmd != IPSET_CMD_PROTOCOL && proto != session->protocol)
8e0608d3
JK
1572 FAILURE("Giving up: kernel protocol version %u "
1573 "does not match our protocol version %u",
b934ebcd 1574 proto, session->protocol);
8e0608d3
JK
1575
1576 D("Message: %s", cmd2name[cmd]);
1577 switch (cmd) {
1578 case IPSET_CMD_LIST:
1579 case IPSET_CMD_SAVE:
1580 ret = callback_list(session, nla, cmd);
1581 D("flag multi: %u", nlh->nlmsg_flags & NLM_F_MULTI);
1582 if (ret >= MNL_CB_STOP && !(nlh->nlmsg_flags & NLM_F_MULTI))
f535c61d 1583 ret = print_set_done(session, false);
8e0608d3
JK
1584 break;
1585 case IPSET_CMD_PROTOCOL:
1586 if (!session->version_checked)
1587 ret = callback_version(session, nla);
1588 break;
1589 case IPSET_CMD_HEADER:
1590 ret = callback_header(session, nla);
1591 break;
1592 case IPSET_CMD_TYPE:
1593 ret = callback_type(session, nla);
1594 break;
1595 default:
1596 FAILURE("Data message received when not expected at %s",
1597 cmd2name[session->cmd]);
1598 }
1599 D("return code: %s", ret == MNL_CB_STOP ? "stop" :
1600 ret == MNL_CB_OK ? "ok" : "error");
1601 return ret;
1602}
1603
1604static int
1605callback_done(const struct nlmsghdr *nlh UNUSED, void *data)
1606{
1607 struct ipset_session *session = data;
1608
1609 D(" called");
1610 if (session->cmd == IPSET_CMD_LIST || session->cmd == IPSET_CMD_SAVE)
f535c61d 1611 return print_set_done(session, true);
3f83fda4 1612
8e0608d3
JK
1613 FAILURE("Invalid message received in non LIST or SAVE state.");
1614}
1615
1616static int
1617decode_errmsg(struct ipset_session *session, const struct nlmsghdr *nlh)
1618{
1619 const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
1620 const struct nlmsghdr *msg = &err->msg;
1621 struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
1622 enum ipset_cmd cmd;
1623 int nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
3f83fda4
JK
1624
1625 if (nlh->nlmsg_len < (uint32_t) MNL_ALIGN(sizeof(struct nlmsgerr)) ||
1626 nlh->nlmsg_len < MNL_ALIGN(sizeof(struct nlmsgerr))
1627 + msg->nlmsg_len)
8e0608d3
JK
1628 FAILURE("Broken error report message received.");
1629
1630 cmd = ipset_get_nlmsg_type(msg);
1631 D("nlsmg_len: %u", msg->nlmsg_len);
1632 if (cmd != session->cmd)
1633 FAILURE("Protocol error, we sent command %s "
1634 "and received error report for %s[%u]",
1635 cmd2name[session->cmd],
1636 cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
1637
1638 if (mnl_attr_parse(msg, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
1639 FAILURE("Broken %s error report message: "
1640 "cannot validate attributes",
1641 cmd2name[cmd]);
1642
1643 if (!nla[IPSET_ATTR_PROTOCOL])
1644 FAILURE("Broken %s error report message: "
1645 "missing protocol attribute",
1646 cmd2name[cmd]);
3f83fda4 1647
8e0608d3
JK
1648 if (nla[IPSET_ATTR_LINENO]) {
1649 session->lineno = mnl_attr_get_u32(nla[IPSET_ATTR_LINENO]);
1650 if (nla[IPSET_ATTR_LINENO]->nla_type & NLA_F_NET_BYTEORDER)
1651 session->lineno = ntohl(session->lineno);
1652 }
3f83fda4 1653
8e0608d3 1654 return ipset_errcode(session, cmd, -err->error);
3f83fda4 1655}
8e0608d3
JK
1656
1657static int
1658callback_error(const struct nlmsghdr *nlh, void *cbdata)
1659{
1660 struct ipset_session *session = cbdata;
1661 struct ipset_data *data = session->data;
1662 const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
1663 int ret = MNL_CB_ERROR;
1664
1665 D(" called, cmd %s", cmd2name[session->cmd]);
1666 if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr)))
1667 FAILURE("Broken error message received.");
1668
1669 if (err->error == 0) {
1670 /* ACK */
1671 ret = MNL_CB_STOP;
1672
1673 switch (session->cmd) {
1674 case IPSET_CMD_CREATE:
1675 /* Add successfully created set to the cache */
1676 ipset_cache_add(ipset_data_setname(data),
3fd6b24a
JK
1677 ipset_data_get(data, IPSET_OPT_TYPE),
1678 ipset_data_family(data));
8e0608d3
JK
1679 break;
1680 case IPSET_CMD_DESTROY:
1681 /* Delete destroyed sets from the cache */
1682 ipset_cache_del(ipset_data_setname(data));
1683 /* Fall through */
1684 case IPSET_CMD_FLUSH:
1685 break;
1686 case IPSET_CMD_RENAME:
1687 ipset_cache_rename(ipset_data_setname(data),
3f83fda4
JK
1688 ipset_data_get(data,
1689 IPSET_OPT_SETNAME2));
8e0608d3
JK
1690 break;
1691 case IPSET_CMD_SWAP:
1692 ipset_cache_swap(ipset_data_setname(data),
3f83fda4
JK
1693 ipset_data_get(data,
1694 IPSET_OPT_SETNAME2));
8e0608d3
JK
1695 break;
1696 case IPSET_CMD_TEST:
1697 if (!(session->envopts & IPSET_ENV_QUIET)) {
3f83fda4
JK
1698 ipset_print_elem(session->report,
1699 IPSET_ERRORBUFLEN,
1700 session->data,
1701 IPSET_OPT_NONE, 0);
8e0608d3
JK
1702 ipset_warn(session, " is in set %s.",
1703 ipset_data_setname(data));
1704 }
1705 /* Fall through */
1706 case IPSET_CMD_ADD:
1707 case IPSET_CMD_DEL:
1708 break;
1709 case IPSET_CMD_LIST:
1710 case IPSET_CMD_SAVE:
1711 /* No set in kernel */
f535c61d 1712 print_set_done(session, true);
8e0608d3
JK
1713 break;
1714 default:
3f83fda4
JK
1715 FAILURE("ACK message received to command %s[%u], "
1716 "which is not expected",
8e0608d3
JK
1717 session->cmd < IPSET_MSG_MAX
1718 ? cmd2name[session->cmd] : "unknown",
1719 session->cmd);
1720 }
1721 return ret;
1722 }
1723 D("nlmsgerr error: %u", -err->error);
1724
1725 /* Error messages */
3f83fda4 1726
8e0608d3 1727 /* Special case for IPSET_CMD_TEST */
3f83fda4
JK
1728 if (session->cmd == IPSET_CMD_TEST &&
1729 err->error == -IPSET_ERR_EXIST) {
8e0608d3
JK
1730 if (!(session->envopts & IPSET_ENV_QUIET)) {
1731 ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
1732 session->data, IPSET_OPT_NONE, 0);
c387170f
JK
1733 ipset_notice(session, " is NOT in set %s.",
1734 ipset_data_setname(data));
8e0608d3
JK
1735 }
1736 return ret;
1737 }
1738
1739 decode_errmsg(session, nlh);
3f83fda4 1740
8e0608d3
JK
1741 return ret;
1742}
1743
1744static int
1745callback_noop(const struct nlmsghdr *nlh UNUSED, void *data UNUSED)
1746{
1747 return MNL_CB_OK;
1748}
1749/*
1750 * Build and send messages
1751 */
1752
d137c0bd 1753static inline int
8e0608d3
JK
1754open_nested(struct ipset_session *session, struct nlmsghdr *nlh, int attr)
1755{
d137c0bd
JK
1756 if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > session->bufsize)
1757 return 1;
3566189c 1758 session->nested[session->nestid++] = mnl_attr_nest_start(nlh, attr);
d137c0bd 1759 return 0;
8e0608d3
JK
1760}
1761
1762static inline void
1763close_nested(struct ipset_session *session, struct nlmsghdr *nlh)
1764{
3566189c 1765 mnl_attr_nest_end(nlh, session->nested[session->nestid-1]);
8e0608d3
JK
1766 session->nested[--session->nestid] = NULL;
1767}
1768
1769static size_t
1770attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
1771{
1772 switch (attr->type) {
0d32c5c0 1773 case MNL_TYPE_NESTED:
8e0608d3
JK
1774 if (attr->len)
1775 return attr->len;
1776
1777 *flags = NLA_F_NET_BYTEORDER;
541e3286 1778 return family == NFPROTO_IPV4 ? sizeof(uint32_t)
8e0608d3 1779 : sizeof(struct in6_addr);
5a602182
JK
1780 case MNL_TYPE_U64:
1781 *flags = NLA_F_NET_BYTEORDER;
1782 return sizeof(uint64_t);
8e0608d3
JK
1783 case MNL_TYPE_U32:
1784 *flags = NLA_F_NET_BYTEORDER;
1785 return sizeof(uint32_t);
1786 case MNL_TYPE_U16:
1787 *flags = NLA_F_NET_BYTEORDER;
1788 return sizeof(uint16_t);
1789 case MNL_TYPE_U8:
1790 return sizeof(uint8_t);
1791 default:
1792 return attr->len;
1793 }
1794}
1795
d137c0bd 1796#define BUFFER_FULL(bufsize, nlmsg_len, nestlen, attrlen) \
3f83fda4
JK
1797(nlmsg_len + nestlen + MNL_ATTR_HDRLEN + MNL_ALIGN(alen) + \
1798 MNL_ALIGN(sizeof(struct nlmsgerr)) > bufsize)
d137c0bd 1799
8e0608d3 1800static int
d137c0bd 1801rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
8e0608d3
JK
1802 const void *d, int type, uint8_t family,
1803 const struct ipset_attr_policy attrs[])
1804{
1805 const struct ipset_attr_policy *attr;
1806 int alen;
1807 uint16_t flags = 0;
1808
1809
1810 attr = &attrs[type];
d137c0bd 1811 if (attr->type == MNL_TYPE_NESTED) {
0d32c5c0 1812 /* IP addresses */
d137c0bd 1813 struct nlattr *nested;
b50666c0
VP
1814
1815 if (type == IPSET_ATTR_BITMASK)
1816 family = ipset_data_family(session->data);
1817
541e3286 1818 int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4
0d32c5c0
JK
1819 : IPSET_ATTR_IPADDR_IPV6;
1820
d137c0bd 1821 alen = attr_len(attr, family, &flags);
3f83fda4
JK
1822 if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len,
1823 MNL_ATTR_HDRLEN, alen))
d137c0bd
JK
1824 return 1;
1825 nested = mnl_attr_nest_start(nlh, type);
541e3286
JE
1826 D("family: %s", family == NFPROTO_IPV4 ? "INET" :
1827 family == NFPROTO_IPV6 ? "INET6" : "UNSPEC");
0d32c5c0
JK
1828 mnl_attr_put(nlh, atype | flags, alen, d);
1829 mnl_attr_nest_end(nlh, nested);
3f83fda4 1830
0d32c5c0
JK
1831 return 0;
1832 }
d137c0bd
JK
1833
1834 alen = attr_len(attr, family, &flags);
1835 if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len, 0, alen))
1836 return 1;
1837
1838 switch (attr->type) {
5a602182
JK
1839 case MNL_TYPE_U64: {
1840 uint64_t value = htobe64(*(const uint64_t *)d);
1841
1842 mnl_attr_put(nlh, type | flags, alen, &value);
1843 return 0;
1844 }
8e0608d3 1845 case MNL_TYPE_U32: {
8dd10256 1846 uint32_t value = htonl(*(const uint32_t *)d);
3f83fda4 1847
a9dcf693
JK
1848 mnl_attr_put(nlh, type | flags, alen, &value);
1849 return 0;
8e0608d3
JK
1850 }
1851 case MNL_TYPE_U16: {
8dd10256 1852 uint16_t value = htons(*(const uint16_t *)d);
3f83fda4 1853
a9dcf693
JK
1854 mnl_attr_put(nlh, type | flags, alen, &value);
1855 return 0;
8e0608d3 1856 }
a9dcf693
JK
1857 case MNL_TYPE_NUL_STRING:
1858 alen = strlen((const char *)d) + 1;
1859 break;
8e0608d3
JK
1860 default:
1861 break;
1862 }
3f83fda4 1863
8e0608d3
JK
1864 mnl_attr_put(nlh, type | flags, alen, d);
1865
1866 return 0;
1867}
1868
1869static int
d137c0bd 1870data2attr(struct ipset_session *session, struct nlmsghdr *nlh,
8e0608d3
JK
1871 struct ipset_data *data, int type, uint8_t family,
1872 const struct ipset_attr_policy attrs[])
1873{
1874 const struct ipset_attr_policy *attr = &attrs[type];
1875
d137c0bd 1876 return rawdata2attr(session, nlh, ipset_data_get(data, attr->opt),
8e0608d3
JK
1877 type, family, attrs);
1878}
1879
b934ebcd
JK
1880#define ADDATTR_PROTOCOL(nlh, protocol) \
1881 mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, protocol)
8e0608d3 1882
d137c0bd
JK
1883#define ADDATTR(session, nlh, data, type, family, attrs) \
1884 data2attr(session, nlh, data, type, family, attrs)
8e0608d3 1885
d137c0bd 1886#define ADDATTR_SETNAME(session, nlh, data) \
3578220c
JK
1887 data2attr(session, nlh, data, IPSET_ATTR_SETNAME, NFPROTO_IPV4, \
1888 cmd_attrs)
8e0608d3 1889
d137c0bd 1890#define ADDATTR_IF(session, nlh, data, type, family, attrs) \
3f83fda4 1891 ipset_data_test(data, attrs[type].opt) ? \
d137c0bd 1892 data2attr(session, nlh, data, type, family, attrs) : 0
8e0608d3 1893
d137c0bd 1894#define ADDATTR_RAW(session, nlh, data, type, attrs) \
541e3286 1895 rawdata2attr(session, nlh, data, type, NFPROTO_IPV4, attrs)
8e0608d3
JK
1896
1897static void
d137c0bd
JK
1898addattr_create(struct ipset_session *session,
1899 struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
8e0608d3
JK
1900{
1901 int i;
1902
1903 for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
d137c0bd 1904 ADDATTR_IF(session, nlh, data, i, family, create_attrs);
8e0608d3
JK
1905}
1906
d137c0bd
JK
1907static int
1908addattr_adt(struct ipset_session *session,
1909 struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
8e0608d3
JK
1910{
1911 int i;
3f83fda4 1912
8e0608d3 1913 for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
d137c0bd
JK
1914 if (ADDATTR_IF(session, nlh, data, i, family, adt_attrs))
1915 return 1;
1916 return 0;
8e0608d3
JK
1917}
1918
1919#define PRIVATE_MSG_BUFLEN 256
1920
1921static int
1922build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
1923{
671b9320 1924 char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)) = {};
8673a740 1925 struct nlmsghdr *nlh = (void *)buffer;
8e0608d3
JK
1926 struct ipset_data *data = session->data;
1927 int len = PRIVATE_MSG_BUFLEN, ret;
1928 enum ipset_cmd saved = session->cmd;
1929
1930 /* Initialize header */
1931 session->transport->fill_hdr(session->handle, cmd, buffer, len, 0);
3f83fda4 1932
b934ebcd
JK
1933 ADDATTR_PROTOCOL(nlh,
1934 cmd == IPSET_CMD_PROTOCOL ? IPSET_PROTOCOL : session->protocol);
8e0608d3
JK
1935
1936 switch (cmd) {
1937 case IPSET_CMD_PROTOCOL:
1938 break;
1939 case IPSET_CMD_HEADER:
1940 if (!ipset_data_test(data, IPSET_SETNAME))
1941 return ipset_err(session,
1942 "Invalid internal HEADER command: "
1943 "missing setname");
d137c0bd 1944 ADDATTR_SETNAME(session, nlh, data);
8e0608d3
JK
1945 break;
1946 case IPSET_CMD_TYPE:
1947 if (!ipset_data_test(data, IPSET_OPT_TYPENAME))
1948 return ipset_err(session,
1949 "Invalid internal TYPE command: "
1950 "missing settype");
3f83fda4 1951 ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
541e3286 1952 NFPROTO_IPV4, cmd_attrs);
8e0608d3 1953 if (ipset_data_test(data, IPSET_OPT_FAMILY))
3f83fda4 1954 ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
541e3286 1955 NFPROTO_IPV4, cmd_attrs);
8e0608d3
JK
1956 else
1957 /* bitmap:port and list:set types */
541e3286 1958 mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, NFPROTO_UNSPEC);
8e0608d3
JK
1959 break;
1960 default:
1961 return ipset_err(session, "Internal error: "
1962 "unknown private command %u", cmd);
1963 }
1964
1965 /* Backup, then restore real command */
1966 session->cmd = cmd;
1967 ret = session->transport->query(session->handle, buffer, len);
1968 session->cmd = saved;
1969
1970 return ret;
1971}
1972
1973static inline bool
d794a110 1974may_aggregate_ad(struct ipset_session *session, enum ipset_cmd cmd)
8e0608d3 1975{
3f83fda4
JK
1976 return session->lineno != 0 &&
1977 (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL) &&
1978 cmd == session->cmd &&
1979 STREQ(ipset_data_setname(session->data), session->saved_setname);
d137c0bd 1980}
8dd10256 1981
d137c0bd 1982static int
8e0608d3
JK
1983build_msg(struct ipset_session *session, bool aggregate)
1984{
8673a740 1985 struct nlmsghdr *nlh = session->buffer;
8e0608d3
JK
1986 struct ipset_data *data = session->data;
1987
1988 /* Public commands */
1989 D("cmd %s, nlmsg_len: %u", cmd2name[session->cmd], nlh->nlmsg_len);
1990 if (nlh->nlmsg_len == 0) {
1991 /* Initialize header */
1992 aggregate = false;
1993 session->transport->fill_hdr(session->handle,
1994 session->cmd,
1995 session->buffer,
1996 session->bufsize,
1997 session->envopts);
b934ebcd 1998 ADDATTR_PROTOCOL(nlh, session->protocol);
8e0608d3
JK
1999 }
2000 D("Protocol added, aggregate %s", aggregate ? "yes" : "no");
2001 switch (session->cmd) {
2002 case IPSET_CMD_CREATE: {
2003 const struct ipset_type *type;
2004
2005 /* Sanity checkings */
2006 if (!ipset_data_test(data, IPSET_SETNAME))
2007 return ipset_err(session,
2008 "Invalid create command: missing setname");
2009 if (!ipset_data_test(data, IPSET_OPT_TYPE))
2010 return ipset_err(session,
2011 "Invalid create command: missing settype");
3f83fda4 2012
8e0608d3
JK
2013 type = ipset_data_get(data, IPSET_OPT_TYPE);
2014 /* Core attributes:
2015 * setname, typename, revision, family, flags (optional) */
d137c0bd 2016 ADDATTR_SETNAME(session, nlh, data);
3f83fda4 2017 ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
541e3286 2018 NFPROTO_IPV4, cmd_attrs);
d137c0bd 2019 ADDATTR_RAW(session, nlh, &type->revision,
8e0608d3 2020 IPSET_ATTR_REVISION, cmd_attrs);
0d32c5c0
JK
2021 D("family: %u, type family %u",
2022 ipset_data_family(data), type->family);
b3858740
JK
2023 if (ipset_data_test(data, IPSET_OPT_FAMILY))
2024 ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
541e3286 2025 NFPROTO_IPV4, cmd_attrs);
b3858740
JK
2026 else
2027 /* bitmap:port and list:set types */
541e3286 2028 mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, NFPROTO_UNSPEC);
8e0608d3
JK
2029
2030 /* Type-specific create attributes */
2031 D("call open_nested");
2032 open_nested(session, nlh, IPSET_ATTR_DATA);
d137c0bd 2033 addattr_create(session, nlh, data, type->family);
8e0608d3
JK
2034 D("call close_nested");
2035 close_nested(session, nlh);
2036 break;
2037 }
2038 case IPSET_CMD_DESTROY:
2039 case IPSET_CMD_FLUSH:
8e0608d3
JK
2040 case IPSET_CMD_SAVE:
2041 if (ipset_data_test(data, IPSET_SETNAME))
d137c0bd 2042 ADDATTR_SETNAME(session, nlh, data);
8e0608d3 2043 break;
bf8463fb
JK
2044 case IPSET_CMD_LIST: {
2045 uint32_t flags = 0;
3f83fda4 2046
bf8463fb
JK
2047 if (session->envopts & IPSET_ENV_LIST_SETNAME)
2048 flags |= IPSET_FLAG_LIST_SETNAME;
2049 if (session->envopts & IPSET_ENV_LIST_HEADER)
2050 flags |= IPSET_FLAG_LIST_HEADER;
2051 if (ipset_data_test(data, IPSET_SETNAME))
2052 ADDATTR_SETNAME(session, nlh, data);
31ee222f 2053 if (flags && session->mode != IPSET_LIST_SAVE) {
bf8463fb 2054 ipset_data_set(data, IPSET_OPT_FLAGS, &flags);
3578220c
JK
2055 ADDATTR(session, nlh, data, IPSET_ATTR_FLAGS,
2056 NFPROTO_IPV4, cmd_attrs);
bf8463fb
JK
2057 }
2058 break;
2059 }
8e0608d3
JK
2060 case IPSET_CMD_RENAME:
2061 case IPSET_CMD_SWAP:
2062 if (!ipset_data_test(data, IPSET_SETNAME))
2063 return ipset_err(session,
2064 "Invalid %s command: missing from-setname",
3f83fda4
JK
2065 session->cmd == IPSET_CMD_SWAP ? "swap" :
2066 "rename");
8e0608d3
JK
2067 if (!ipset_data_test(data, IPSET_OPT_SETNAME2))
2068 return ipset_err(session,
2069 "Invalid %s command: missing to-setname",
3f83fda4
JK
2070 session->cmd == IPSET_CMD_SWAP ? "swap" :
2071 "rename");
d137c0bd 2072 ADDATTR_SETNAME(session, nlh, data);
3f83fda4
JK
2073 ADDATTR_RAW(session, nlh,
2074 ipset_data_get(data, IPSET_OPT_SETNAME2),
8e0608d3
JK
2075 IPSET_ATTR_SETNAME2, cmd_attrs);
2076 break;
2077 case IPSET_CMD_ADD:
2078 case IPSET_CMD_DEL: {
cd5ede17 2079 DD(const struct ipset_type *type);
8e0608d3
JK
2080
2081 if (!aggregate) {
2082 /* Setname, type not checked/added yet */
2083 if (!ipset_data_test(data, IPSET_SETNAME))
2084 return ipset_err(session,
2085 "Invalid %s command: missing setname",
3f83fda4
JK
2086 session->cmd == IPSET_CMD_ADD ? "add" :
2087 "del");
8e0608d3
JK
2088
2089 if (!ipset_data_test(data, IPSET_OPT_TYPE))
2090 return ipset_err(session,
2091 "Invalid %s command: missing settype",
3f83fda4
JK
2092 session->cmd == IPSET_CMD_ADD ? "add" :
2093 "del");
8e0608d3
JK
2094
2095 /* Core options: setname */
d137c0bd 2096 ADDATTR_SETNAME(session, nlh, data);
8e0608d3
JK
2097 if (session->lineno != 0) {
2098 /* Restore mode */
d137c0bd 2099 ADDATTR_RAW(session, nlh, &session->lineno,
8e0608d3
JK
2100 IPSET_ATTR_LINENO, cmd_attrs);
2101 open_nested(session, nlh, IPSET_ATTR_ADT);
2102 }
2103 }
cd5ede17 2104 DD(type = ipset_data_get(data, IPSET_OPT_TYPE));
0d32c5c0
JK
2105 D("family: %u, type family %u",
2106 ipset_data_family(data), type->family);
d137c0bd
JK
2107 if (open_nested(session, nlh, IPSET_ATTR_DATA)) {
2108 D("open_nested failed");
2109 return 1;
2110 }
3f83fda4
JK
2111 if (addattr_adt(session, nlh, data, ipset_data_family(data)) ||
2112 ADDATTR_RAW(session, nlh, &session->lineno,
2113 IPSET_ATTR_LINENO, cmd_attrs)) {
d137c0bd 2114 /* Cancel last, unfinished nested attribute */
3f83fda4
JK
2115 mnl_attr_nest_cancel(nlh,
2116 session->nested[session->nestid-1]);
d137c0bd
JK
2117 session->nested[--session->nestid] = NULL;
2118 return 1;
2119 }
8e0608d3
JK
2120 close_nested(session, nlh);
2121 break;
2122 }
2123 case IPSET_CMD_TEST: {
cd5ede17 2124 DD(const struct ipset_type *type);
8e0608d3
JK
2125 /* Return codes are not aggregated, so tests cannot be either */
2126
2127 /* Setname, type not checked/added yet */
3f83fda4 2128
8e0608d3
JK
2129 if (!ipset_data_test(data, IPSET_SETNAME))
2130 return ipset_err(session,
2131 "Invalid test command: missing setname");
2132
2133 if (!ipset_data_test(data, IPSET_OPT_TYPE))
2134 return ipset_err(session,
2135 "Invalid test command: missing settype");
3f83fda4 2136
cd5ede17 2137 DD(type = ipset_data_get(data, IPSET_OPT_TYPE));
0d32c5c0
JK
2138 D("family: %u, type family %u",
2139 ipset_data_family(data), type->family);
d137c0bd 2140 ADDATTR_SETNAME(session, nlh, data);
8e0608d3 2141 open_nested(session, nlh, IPSET_ATTR_DATA);
d137c0bd 2142 addattr_adt(session, nlh, data, ipset_data_family(data));
8e0608d3
JK
2143 close_nested(session, nlh);
2144 break;
2145 }
2146 default:
2147 return ipset_err(session, "Internal error: unknown command %u",
2148 session->cmd);
2149 }
2150 return 0;
2151}
2152
2153/**
2154 * ipset_commit - commit buffered commands
2155 * @session: session structure
2156 *
2157 * Commit buffered commands, if there are any.
2158 *
2159 * Returns 0 on success or a negative error code.
2160 */
2161int
2162ipset_commit(struct ipset_session *session)
2163{
2164 struct nlmsghdr *nlh;
2165 int ret = 0, i;
2166
2167 assert(session);
2168
8673a740 2169 nlh = session->buffer;
3f83fda4
JK
2170 D("send buffer: len %u, cmd %s",
2171 nlh->nlmsg_len, cmd2name[session->cmd]);
8e0608d3
JK
2172 if (nlh->nlmsg_len == 0)
2173 /* Nothing to do */
2174 return 0;
2175
2176 /* Close nested data blocks */
2177 for (i = session->nestid - 1; i >= 0; i--)
2178 close_nested(session, nlh);
2179
2180 /* Send buffer */
2181 ret = session->transport->query(session->handle,
2182 session->buffer,
2183 session->bufsize);
2184
d794a110
JK
2185 /* Reset saved data and nested state */
2186 session->saved_setname[0] = '\0';
31ee222f 2187 session->printed_set = 0;
8e0608d3
JK
2188 for (i = session->nestid - 1; i >= 0; i--)
2189 session->nested[i] = NULL;
2190 session->nestid = 0;
2191 nlh->nlmsg_len = 0;
2192
2193 D("ret: %d", ret);
2194
2195 if (ret < 0) {
2196 if (session->report[0] != '\0')
2197 return -1;
2198 else
2199 return ipset_err(session,
3f83fda4 2200 "Internal protocol error");
8e0608d3
JK
2201 }
2202 return 0;
2203}
2204
2205static mnl_cb_t cb_ctl[] = {
2206 [NLMSG_NOOP] = callback_noop,
2207 [NLMSG_ERROR] = callback_error,
2208 [NLMSG_DONE] = callback_done,
2209 [NLMSG_OVERRUN] = callback_noop,
2210 [NLMSG_MIN_TYPE] = callback_data,
2211};
3f83fda4 2212
8e0608d3
JK
2213static inline struct ipset_handle *
2214init_transport(struct ipset_session *session)
2215{
2216 session->handle = session->transport->init(cb_ctl, session);
2217
2218 return session->handle;
2219}
2220
2221/**
2222 * ipset_cmd - execute a command
2223 * @session: session structure
2224 * @cmd: command to execute
2225 * @lineno: command line number in restore mode
2226 *
2227 * Execute - or prepare/buffer in restore mode - a command.
2228 * It is the caller responsibility that the data field be filled out
2229 * with all required parameters for a successful execution.
2230 * The data field is cleared after this function call for the public
2231 * commands.
2232 *
2233 * Returns 0 on success or a negative error code.
2234 */
2235int
2236ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
2237{
2238 struct ipset_data *data;
2239 bool aggregate = false;
2240 int ret = -1;
3f83fda4 2241
8e0608d3
JK
2242 assert(session);
2243
b934ebcd 2244 if (cmd < IPSET_CMD_NONE || cmd >= IPSET_MSG_MAX)
8e0608d3
JK
2245 return 0;
2246
2247 /* Initialize transport method if not done yet */
2248 if (session->handle == NULL && init_transport(session) == NULL)
2249 return ipset_err(session,
2250 "Cannot open session to kernel.");
2251
2252 data = session->data;
2253
2254 /* Check protocol version once */
2255 if (!session->version_checked) {
2256 if (build_send_private_msg(session, IPSET_CMD_PROTOCOL) < 0)
2257 return -1;
b934ebcd
JK
2258 if (ipset_session_report_type(session) == IPSET_WARNING &&
2259 cmd != IPSET_CMD_NONE)
2260 /* Suppress protocol warning */
2261 ipset_session_report_reset(session);
8e0608d3 2262 }
b934ebcd
JK
2263 /* IPSET_CMD_NONE: check protocol version only */
2264 if (cmd == IPSET_CMD_NONE)
2265 return 0;
8e0608d3
JK
2266
2267 /* Private commands */
3f83fda4 2268 if (cmd == IPSET_CMD_TYPE || cmd == IPSET_CMD_HEADER)
8e0608d3 2269 return build_send_private_msg(session, cmd);
3f83fda4 2270
d137c0bd 2271 /* Check aggregatable commands */
d794a110
JK
2272 aggregate = may_aggregate_ad(session, cmd);
2273 if (!aggregate) {
2274 /* Flush possible aggregated commands */
2275 ret = ipset_commit(session);
2276 if (ret < 0)
2277 return ret;
2278 }
8e0608d3
JK
2279
2280 /* Real command: update lineno too */
2281 session->cmd = cmd;
2282 session->lineno = lineno;
3f83fda4 2283
ca62c0e1
JK
2284 if (cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) {
2285 /* Set default output mode */
8e0608d3
JK
2286 if (session->mode == IPSET_LIST_NONE)
2287 session->mode = IPSET_LIST_PLAIN;
ca62c0e1
JK
2288 /* Reset just in case there are multiple modes in a session */
2289 ipset_envopt_unset(session, IPSET_ENV_QUOTED);
2290 switch (session->mode) {
2291 case IPSET_LIST_XML:
2292 /* Start the root element in XML mode */
2293 safe_snprintf(session, "<ipsets>\n");
2294 break;
2295 case IPSET_LIST_JSON:
2296 /* Start the root element in json mode */
2297 ipset_envopt_set(session, IPSET_ENV_QUOTED);
2298 safe_snprintf(session, "[\n");
2299 break;
2300 default:
2301 break;
2302 }
8e0608d3 2303 }
2885607f 2304
8e0608d3
JK
2305 D("next: build_msg");
2306 /* Build new message or append buffered commands */
d137c0bd
JK
2307 ret = build_msg(session, aggregate);
2308 D("build_msg returned %u", ret);
2309 if (ret > 0) {
2310 /* Buffer is full, send buffered commands */
d794a110
JK
2311 ret = ipset_commit(session);
2312 if (ret < 0)
d137c0bd
JK
2313 goto cleanup;
2314 ret = build_msg(session, false);
2315 D("build_msg 2 returned %u", ret);
2316 }
2317 if (ret < 0)
8e0608d3
JK
2318 goto cleanup;
2319 D("past: build_msg");
2320
020936c8
JK
2321 /* We have to save the type for error handling */
2322 session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE);
3f83fda4
JK
2323 if (session->lineno != 0 &&
2324 (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
d794a110 2325 /* Save setname for the next possible aggregated restore line */
8e0608d3 2326 strcpy(session->saved_setname, ipset_data_setname(data));
d794a110 2327 ipset_data_reset(data);
8e0608d3 2328 /* Don't commit: we may aggregate next command */
d794a110
JK
2329 ret = 0;
2330 goto cleanup;
8e0608d3 2331 }
d794a110 2332
3f83fda4 2333 D("call commit");
d794a110
JK
2334 ret = ipset_commit(session);
2335
8e0608d3
JK
2336cleanup:
2337 D("reset data");
2338 ipset_data_reset(data);
2339 return ret;
2340}
2341
55fdd96e
JK
2342static
2343int __attribute__ ((format (printf, 3, 4)))
2344default_print_outfn(struct ipset_session *session, void *p UNUSED,
2345 const char *fmt, ...)
2346{
2347 int len;
2348 va_list args;
2349
2350 va_start(args, fmt);
2351 len = vfprintf(session->ostream, fmt, args);
2352 va_end(args);
2353
2354 return len;
2355}
2356
0907c31d 2357/**
55fdd96e
JK
2358 * ipset_session_print_outfn - set session output printing function
2359 * @session: session structure
2360 * @outfn: output printing function
2361 * @p: pointer to private area
0907c31d 2362 *
55fdd96e
JK
2363 * Set the session output printing function. If the @outfn is NULL,
2364 * then the default output function is configured. You can set
2365 * the @p pointer to a private area: the output printing function
2366 * is called with @p in one of its arguments.
0907c31d 2367 *
55fdd96e 2368 * Returns 0 on success or a negative error code.
0907c31d
JK
2369 */
2370int
55fdd96e
JK
2371ipset_session_print_outfn(struct ipset_session *session,
2372 ipset_print_outfn outfn,
2373 void *p)
0907c31d 2374{
55fdd96e
JK
2375 session->print_outfn = outfn ? outfn : default_print_outfn;
2376 session->p = p;
0907c31d
JK
2377 return 0;
2378}
2379
8e0608d3
JK
2380/**
2381 * ipset_session_init - initialize an ipset session
55fdd96e
JK
2382 * @outfn: output printing function
2383 * @p: pointer to private area
8e0608d3
JK
2384 *
2385 * Initialize an ipset session by allocating a session structure
55fdd96e
JK
2386 * and filling out with the initialization data. The function
2387 * calls ipset_session_print_outfn() to set @print_outfn, @p.
8e0608d3
JK
2388 *
2389 * Returns the created session sctructure on success or NULL.
2390 */
2391struct ipset_session *
55fdd96e 2392ipset_session_init(ipset_print_outfn print_outfn, void *p)
8e0608d3
JK
2393{
2394 struct ipset_session *session;
2395 size_t bufsize = getpagesize();
8dd10256 2396
8e0608d3
JK
2397 /* Create session object */
2398 session = calloc(1, sizeof(struct ipset_session) + bufsize);
2399 if (session == NULL)
2400 return NULL;
3713072d
JK
2401 session->outbuf = calloc(1, IPSET_OUTBUFLEN);
2402 if (session->outbuf == NULL)
2403 goto free_session;
2404 session->outbuflen = IPSET_OUTBUFLEN;
8e0608d3 2405 session->bufsize = bufsize;
8dd10256 2406 session->buffer = session + 1;
55fdd96e
JK
2407 session->istream = stdin;
2408 session->ostream = stdout;
b934ebcd 2409 session->protocol = IPSET_PROTOCOL;
3713072d
JK
2410 INIT_LIST_HEAD(&session->sorted);
2411 INIT_LIST_HEAD(&session->pool);
8e0608d3
JK
2412
2413 /* The single transport method yet */
2414 session->transport = &ipset_mnl_transport;
3f83fda4 2415
8e0608d3 2416 /* Output function */
55fdd96e 2417 ipset_session_print_outfn(session, print_outfn, p);
3f83fda4 2418
8e0608d3
JK
2419 /* Initialize data structures */
2420 session->data = ipset_data_init();
2421 if (session->data == NULL)
3713072d 2422 goto free_outbuf;
8e0608d3 2423
3fd6b24a 2424 ipset_cache_init();
8e0608d3
JK
2425 return session;
2426
3713072d
JK
2427free_outbuf:
2428 free(session->outbuf);
8e0608d3 2429free_session:
3f83fda4
JK
2430 free(session);
2431 return NULL;
8e0608d3
JK
2432}
2433
55fdd96e
JK
2434/**
2435 * ipset_session_io_full - set full IO for the session
2436 * @session: session structure
2437 * @filename: filename
2438 * @what: operate on input/output
2439 *
2440 * The normal "-file" CLI interface does not provide an interface
2441 * to set both the input (restore) and output (list/save) for
2442 * a session. This function makes it possible to configure those.
2443 *
2444 * When a filename for input is passed, then the file will be opened
2445 * for reading.
2446 * When a filename for output is passed, then the file will be opened
2447 * for writing.
2448 * Previously opened files are closed.
2449 * If NULL is passed as filename, stdin/stdout is set.
2450 * Input/output files can be set separatedly.
2451 * The function returns error if the file cannot be opened or
2452 * normal IO mode is already set.
2453 *
2454 * Returns 0 on success or a negative error code.
2455 */
2456int
2457ipset_session_io_full(struct ipset_session *session, const char *filename,
2458 enum ipset_io_type what)
2459{
2460 FILE *f;
2461
2462 assert(session);
2463
2464 if (session->normal_io)
2465 return ipset_err(session,
2466 "Normal IO is in use, full IO cannot be selected");
2467
2468 switch (what) {
2469 case IPSET_IO_INPUT:
2470 if (session->istream != stdin)
2471 fclose(session->istream);
2472 if (!filename) {
2473 session->istream = stdin;
2474 } else {
2475 f = fopen(filename, "r");
2476 if (!f)
2477 return ipset_err(session,
2478 "Cannot open %s for reading: %s",
2479 filename, strerror(errno));
2480 session->istream = f;
2481 }
2482 break;
2483 case IPSET_IO_OUTPUT:
2484 if (session->ostream != stdout)
2485 fclose(session->ostream);
2486 if (!filename) {
2487 session->ostream = stdout;
2488 } else {
2489 f = fopen(filename, "w");
2490 if (!f)
2491 return ipset_err(session,
2492 "Cannot open %s for writing: %s",
2493 filename, strerror(errno));
2494 session->ostream = f;
2495 }
2496 break;
2497 default:
2498 return ipset_err(session,
2499 "Library error, invalid ipset_io_type");
2500 }
2501 session->full_io = !(session->istream == stdin &&
2502 session->ostream == stdout);
2503 return 0;
2504}
2505
2506/**
2507 * ipset_session_io_normal - set normal IO for the session
2508 * @session: session structure
2509 * @filename: filename
2510 * @what: operate on input/output
2511 *
2512 * The normal "-file" CLI interface to set either the input (restore)
2513 * or output (list/save) for a session. This function does not make
2514 * possible to set both independently.
2515 *
2516 * When a filename for input is passed, then the file will be opened
2517 * for reading.
2518 * When a filename for output is passed, then the file will be opened
2519 * for writing.
2520 * Previously opened files are closed.
2521 * If NULL is passed as filename, stdin/stdout is set.
2522 * Input/output files cannot be set separatedly.
2523 * The function returns error if the file cannot be opened or
2524 * full IO mode is already set.
2525 *
2526 * Returns 0 on success or a negative error code.
2527 */
2528int
2529ipset_session_io_normal(struct ipset_session *session, const char *filename,
2530 enum ipset_io_type what)
2531{
2532 FILE *f;
2533
2534 assert(session);
2535 assert(filename);
2536
2537 if (session->full_io)
2538 return ipset_err(session,
2539 "Full IO is in use, normal IO cannot be selected");
2540 if (session->istream != stdin) {
2541 fclose(session->istream);
2542 session->istream = stdin;
2543 }
2544 if (session->ostream != stdout) {
2545 fclose(session->ostream);
2546 session->ostream = stdout;
2547 }
2548 switch (what) {
2549 case IPSET_IO_INPUT:
2550 f = fopen(filename, "r");
2551 if (!f)
2552 return ipset_err(session,
2553 "Cannot open %s for reading: %s",
2554 filename, strerror(errno));
2555 session->istream = f;
2556 break;
2557 case IPSET_IO_OUTPUT:
2558 f = fopen(filename, "w");
2559 if (!f)
2560 return ipset_err(session,
2561 "Cannot open %s for writing: %s",
2562 filename, strerror(errno));
2563 session->ostream = f;
2564 break;
2565 default:
2566 return ipset_err(session,
2567 "Library error, invalid ipset_io_type");
2568 }
2569 session->normal_io = !(session->istream == stdin &&
2570 session->ostream == stdout);
2571 return 0;
2572}
2573
2574/**
2575 * ipset_session_io_stream - returns the input or output stream
2576 * @what: operate on input/output
2577 *
2578 * Returns the input or output stream of the session.
2579 */
2580FILE *
2581ipset_session_io_stream(struct ipset_session *session,
2582 enum ipset_io_type what)
2583{
2584 switch (what) {
2585 case IPSET_IO_INPUT:
2586 return session->istream;
2587 case IPSET_IO_OUTPUT:
2588 return session->ostream;
2589 default:
2590 return NULL;
2591 }
2592}
2593
2594/**
2595 * ipset_session_io_close - closes the input or output stream
2596 * @what: operate on input/output
2597 *
2598 * Closes the input or output stream of the session.
2599 *
2600 * Returns 0 on success or a negative error code.
2601 */
2602int
2603ipset_session_io_close(struct ipset_session *session,
2604 enum ipset_io_type what)
2605{
2606 switch (what) {
2607 case IPSET_IO_INPUT:
2608 if (session->istream != stdin) {
2609 fclose(session->istream);
2610 session->istream = stdin;
2611 }
2612 break;
2613 case IPSET_IO_OUTPUT:
2614 if (session->ostream != stdout) {
2615 fclose(session->ostream);
2616 session->ostream = stdout;
2617 }
2618 break;
2619 default:
2620 break;
2621 }
2622 return 0;
2623}
2624
8e0608d3
JK
2625/**
2626 * ipset_session_fini - destroy an ipset session
2627 * @session: session structure
2628 *
2629 * Destroy an ipset session: release the created structures.
2630 *
2631 * Returns 0 on success or a negative error code.
2632 */
2633int
2634ipset_session_fini(struct ipset_session *session)
2635{
3713072d 2636 struct ipset_sorted *pos, *n;
8e0608d3
JK
2637 assert(session);
2638
2639 if (session->handle)
2640 session->transport->fini(session->handle);
2641 if (session->data)
2642 ipset_data_fini(session->data);
55fdd96e
JK
2643 if (session->istream != stdin)
2644 fclose(session->istream);
2645 if (session->ostream != stdout)
2646 fclose(session->ostream);
8e0608d3 2647
3fd6b24a 2648 ipset_cache_fini();
3713072d
JK
2649
2650 list_for_each_entry_safe(pos, n, &session->sorted, list) {
2651 list_del(&pos->list);
2652 free(pos);
2653 }
2654 list_for_each_entry_safe(pos, n, &session->pool, list) {
2655 list_del(&pos->list);
2656 free(pos);
2657 }
2658 free(session->outbuf);
8e0608d3
JK
2659 free(session);
2660 return 0;
2661}
eb77f0db
JK
2662
2663#ifdef IPSET_DEBUG
2664#include "debug.c"
2665#endif