1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdlib.h>
27 #include <strings.h>
28 #include <stddef.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <sys/list.h>
35 #include <net/if.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <libintl.h>
39 #include <libilb.h>
40 #include <inet/ilb.h>
41 #include "libilb_impl.h"
42 #include "ilbd.h"
43
44 /* until we all use AF_* macros ... */
45 #define AF_2_IPPROTO(_af) (_af == AF_INET)?IPPROTO_IP:IPPROTO_IPV6
46 #define IPPROTO_2_AF(_i) (_i == IPPROTO_IP)?AF_INET:AF_INET6
47
48 #define PROTOCOL_LEN 16 /* protocol type */
49 #define ADDR_LEN (2 * INET6_ADDRSTRLEN + 1) /* prxy src range */
50 #define PORT_LEN 6 /* hcport:1-65535 or "ANY" */
51
52 static ilb_status_t ilbd_disable_one_rule(ilbd_rule_t *, boolean_t);
53 static uint32_t i_flags_d2k(int);
54
55 #define ILB_SGSRV_2_KSRV(s, k) \
56 (k)->addr = (s)->sgs_addr; \
57 (k)->min_port = (s)->sgs_minport; \
58 (k)->max_port = (s)->sgs_maxport; \
59 (k)->flags = i_flags_d2k((s)->sgs_flags); \
60 (k)->err = 0; \
61 (void) strlcpy((k)->name, (s)->sgs_srvID, sizeof ((k)->name))
62
63 list_t ilbd_rule_hlist;
64
65 static ilb_algo_t
algo_impl2lib(ilb_algo_impl_t a)66 algo_impl2lib(ilb_algo_impl_t a)
67 {
68 switch (a) {
69 case ILB_ALG_IMPL_ROUNDROBIN:
70 return (ILB_ALG_ROUNDROBIN);
71 case ILB_ALG_IMPL_HASH_IP:
72 return (ILB_ALG_HASH_IP);
73 case ILB_ALG_IMPL_HASH_IP_SPORT:
74 return (ILB_ALG_HASH_IP_SPORT);
75 case ILB_ALG_IMPL_HASH_IP_VIP:
76 return (ILB_ALG_HASH_IP_VIP);
77 }
78 return (0);
79 }
80
81 static ilb_topo_t
topo_impl2lib(ilb_topo_impl_t t)82 topo_impl2lib(ilb_topo_impl_t t)
83 {
84 switch (t) {
85 case ILB_TOPO_IMPL_DSR:
86 return (ILB_TOPO_DSR);
87 case ILB_TOPO_IMPL_NAT:
88 return (ILB_TOPO_NAT);
89 case ILB_TOPO_IMPL_HALF_NAT:
90 return (ILB_TOPO_HALF_NAT);
91 }
92 return (0);
93 }
94
95 ilb_algo_impl_t
algo_lib2impl(ilb_algo_t a)96 algo_lib2impl(ilb_algo_t a)
97 {
98 switch (a) {
99 case ILB_ALG_ROUNDROBIN:
100 return (ILB_ALG_IMPL_ROUNDROBIN);
101 case ILB_ALG_HASH_IP:
102 return (ILB_ALG_IMPL_HASH_IP);
103 case ILB_ALG_HASH_IP_SPORT:
104 return (ILB_ALG_IMPL_HASH_IP_SPORT);
105 case ILB_ALG_HASH_IP_VIP:
106 return (ILB_ALG_IMPL_HASH_IP_VIP);
107 }
108 return (0);
109 }
110
111 ilb_topo_impl_t
topo_lib2impl(ilb_topo_t t)112 topo_lib2impl(ilb_topo_t t)
113 {
114 switch (t) {
115 case ILB_TOPO_DSR:
116 return (ILB_TOPO_IMPL_DSR);
117 case ILB_TOPO_NAT:
118 return (ILB_TOPO_IMPL_NAT);
119 case ILB_TOPO_HALF_NAT:
120 return (ILB_TOPO_IMPL_HALF_NAT);
121 }
122 return (0);
123 }
124
125 /*
126 * Walk the list of rules and check if its safe to add the
127 * the server to the rule (this is a list of rules hanging
128 * off of a server group)
129 */
130 ilb_status_t
i_check_srv2rules(list_t * rlist,ilb_sg_srv_t * srv)131 i_check_srv2rules(list_t *rlist, ilb_sg_srv_t *srv)
132 {
133 ilb_status_t rc = ILB_STATUS_OK;
134 ilbd_rule_t *rl;
135 int server_portrange, rule_portrange;
136 int srv_minport, srv_maxport;
137 int r_minport, r_maxport;
138
139 if (srv == NULL)
140 return (ILB_STATUS_OK);
141
142 srv_minport = ntohs(srv->sgs_minport);
143 srv_maxport = ntohs(srv->sgs_maxport);
144
145 for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
146 r_minport = ntohs(rl->irl_minport);
147 r_maxport = ntohs(rl->irl_maxport);
148
149 if ((srv_minport != 0) && (srv_minport == srv_maxport)) {
150 /* server has single port */
151 if (rl->irl_topo == ILB_TOPO_DSR) {
152 /*
153 * either we have a DSR rule with a port
154 * range, or both server and rule
155 * have single ports but their values
156 * don't match - this is incompatible
157 */
158 if (r_maxport > r_minport) {
159 rc = ILB_STATUS_INVAL_SRVR;
160 break;
161 } else if (srv_minport != r_minport) {
162 rc = ILB_STATUS_BADPORT;
163 break;
164 }
165 }
166 if (rl->irl_hcpflag == ILB_HCI_PROBE_FIX &&
167 rl->irl_hcport != srv_minport) {
168 rc = ILB_STATUS_BADPORT;
169 break;
170 }
171 } else if (srv_maxport > srv_minport) {
172 /* server has a port range */
173 if ((rl->irl_topo == ILB_TOPO_DSR) &&
174 (r_maxport > r_minport)) {
175 if ((r_minport != srv_minport) ||
176 (r_maxport != srv_maxport)) {
177 /*
178 * we have a DSR rule with a port range
179 * and its min and max port values
180 * does not meet that of server's
181 * - this is incompatible
182 */
183 rc = ILB_STATUS_BADPORT;
184 break;
185 }
186 } else if ((rl->irl_topo == ILB_TOPO_DSR) &&
187 (r_maxport == r_minport)) {
188 /*
189 * we have a DSR rule with a single
190 * port and a server with a port range
191 * - this is incompatible
192 */
193 rc = ILB_STATUS_INVAL_SRVR;
194 break;
195 } else if (((rl->irl_topo == ILB_TOPO_NAT) ||
196 (rl->irl_topo == ILB_TOPO_HALF_NAT)) &&
197 (r_maxport > r_minport)) {
198 server_portrange = srv_maxport - srv_minport;
199 rule_portrange = r_maxport - r_minport;
200 if (rule_portrange != server_portrange) {
201 /*
202 * we have a NAT/Half-NAT rule with
203 * a port range and server with a port
204 * range and there is a mismatch in the
205 * sizes of the port ranges - this is
206 * incompatible
207 */
208 rc = ILB_STATUS_INVAL_SRVR;
209 break;
210 }
211 }
212 if (rl->irl_hcpflag == ILB_HCI_PROBE_FIX &&
213 (rl->irl_hcport > srv_maxport ||
214 rl->irl_hcport < srv_minport)) {
215 rc = ILB_STATUS_BADPORT;
216 break;
217 }
218 }
219 }
220
221 return (rc);
222 }
223
224 void
i_setup_rule_hlist(void)225 i_setup_rule_hlist(void)
226 {
227 list_create(&ilbd_rule_hlist, sizeof (ilbd_rule_t),
228 offsetof(ilbd_rule_t, irl_link));
229 }
230
231 ilb_status_t
i_ilbd_save_rule(ilbd_rule_t * irl,ilbd_scf_cmd_t scf_cmd)232 i_ilbd_save_rule(ilbd_rule_t *irl, ilbd_scf_cmd_t scf_cmd)
233 {
234 boolean_t enable = irl->irl_flags & ILB_FLAGS_RULE_ENABLED;
235
236 switch (scf_cmd) {
237 case ILBD_SCF_CREATE:
238 return (ilbd_create_pg(ILBD_SCF_RULE, (void *)irl));
239 case ILBD_SCF_DESTROY:
240 return (ilbd_destroy_pg(ILBD_SCF_RULE, irl->irl_name));
241 case ILBD_SCF_ENABLE_DISABLE:
242 return (ilbd_change_prop(ILBD_SCF_RULE, irl->irl_name,
243 "status", &enable));
244 default:
245 logdebug("i_ilbd_save_rule: invalid scf cmd %d", scf_cmd);
246 return (ILB_STATUS_INVAL_CMD);
247 }
248 }
249
250 /*
251 * allocate a new daemon-specific rule from the "template" passed
252 * in in *r
253 */
254 static ilbd_rule_t *
i_alloc_ilbd_rule(ilb_rule_info_t * r)255 i_alloc_ilbd_rule(ilb_rule_info_t *r)
256 {
257 ilbd_rule_t *rl;
258
259 rl = calloc(sizeof (*rl), 1);
260 if (rl != NULL && r != NULL)
261 bcopy(r, &rl->irl_info, sizeof (*r));
262
263 return (rl);
264 }
265
266 static ilbd_rule_t *
i_find_rule_byname(const char * name)267 i_find_rule_byname(const char *name)
268 {
269 ilbd_rule_t *rl;
270
271 /* find position of rule in list */
272 rl = list_head(&ilbd_rule_hlist);
273 while (rl != NULL &&
274 strncmp(rl->irl_name, name, sizeof (rl->irl_name)) != 0) {
275 rl = list_next(&ilbd_rule_hlist, rl);
276 }
277
278 return (rl);
279 }
280
281 /*
282 * get exactly one rule (named in rl->irl_name) data from kernel
283 */
284 static ilb_status_t
ilb_get_krule(ilb_rule_info_t * rl)285 ilb_get_krule(ilb_rule_info_t *rl)
286 {
287 ilb_status_t rc;
288 ilb_rule_cmd_t kcmd;
289
290 kcmd.cmd = ILB_LIST_RULE;
291 (void) strlcpy(kcmd.name, rl->rl_name, sizeof (kcmd.name));
292 kcmd.flags = 0;
293
294 rc = do_ioctl(&kcmd, 0);
295 if (rc != ILB_STATUS_OK)
296 return (rc);
297
298 rl->rl_flags = kcmd.flags;
299 rl->rl_ipversion = IPPROTO_2_AF(kcmd.ip_ver);
300 rl->rl_vip = kcmd.vip;
301 rl->rl_proto = kcmd.proto;
302 rl->rl_minport = kcmd.min_port;
303 rl->rl_maxport = kcmd.max_port;
304 rl->rl_algo = algo_impl2lib(kcmd.algo);
305 rl->rl_topo = topo_impl2lib(kcmd.topo);
306 rl->rl_stickymask = kcmd.sticky_mask;
307 rl->rl_nat_src_start = kcmd.nat_src_start;
308 rl->rl_nat_src_end = kcmd.nat_src_end;
309 (void) strlcpy(rl->rl_name, kcmd.name, sizeof (rl->rl_name));
310 rl->rl_conndrain = kcmd.conn_drain_timeout;
311 rl->rl_nat_timeout = kcmd.nat_expiry;
312 rl->rl_sticky_timeout = kcmd.sticky_expiry;
313
314 return (ILB_STATUS_OK);
315 }
316
317 ilb_status_t
ilbd_retrieve_rule(ilbd_name_t rl_name,uint32_t * rbuf,size_t * rbufsz)318 ilbd_retrieve_rule(ilbd_name_t rl_name, uint32_t *rbuf, size_t *rbufsz)
319 {
320 ilbd_rule_t *irl = NULL;
321 ilb_status_t rc;
322 ilb_rule_info_t *rinfo;
323
324 irl = i_find_rule_byname(rl_name);
325 if (irl == NULL)
326 return (ILB_STATUS_ENOENT);
327
328 ilbd_reply_ok(rbuf, rbufsz);
329 rinfo = (ilb_rule_info_t *)&((ilb_comm_t *)rbuf)->ic_data;
330 bcopy(&irl->irl_info, rinfo, sizeof (*rinfo));
331
332 /*
333 * Check if the various timeout values are 0. If one is, get the
334 * default values from kernel.
335 */
336 if (rinfo->rl_conndrain == 0 || rinfo->rl_nat_timeout == 0 ||
337 rinfo->rl_sticky_timeout == 0) {
338 ilb_rule_info_t tmp_info;
339
340 (void) strcpy(tmp_info.rl_name, rinfo->rl_name);
341 rc = ilb_get_krule(&tmp_info);
342 if (rc != ILB_STATUS_OK)
343 return (rc);
344 if (rinfo->rl_conndrain == 0)
345 rinfo->rl_conndrain = tmp_info.rl_conndrain;
346 if ((rinfo->rl_topo == ILB_TOPO_NAT ||
347 rinfo->rl_topo == ILB_TOPO_HALF_NAT) &&
348 rinfo->rl_nat_timeout == 0) {
349 rinfo->rl_nat_timeout = tmp_info.rl_nat_timeout;
350 }
351 if ((rinfo->rl_flags & ILB_FLAGS_RULE_STICKY) &&
352 rinfo->rl_sticky_timeout == 0) {
353 rinfo->rl_sticky_timeout = tmp_info.rl_sticky_timeout;
354 }
355 }
356 *rbufsz += sizeof (ilb_rule_info_t);
357
358 return (ILB_STATUS_OK);
359 }
360
361 static ilb_status_t
ilbd_destroy_one_rule(ilbd_rule_t * irl)362 ilbd_destroy_one_rule(ilbd_rule_t *irl)
363 {
364 ilb_status_t rc;
365 ilb_name_cmd_t kcmd;
366
367 /*
368 * as far as talking to the kernel is concerned, "all rules"
369 * is handled in one go somewhere else, so we only
370 * tell the kernel about single rules here.
371 */
372 if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
373 kcmd.cmd = ILB_DESTROY_RULE;
374 (void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
375 kcmd.flags = 0;
376
377 rc = do_ioctl(&kcmd, 0);
378 if (rc != ILB_STATUS_OK)
379 return (rc);
380
381 }
382 list_remove(&irl->irl_sg->isg_rulelist, irl);
383 list_remove(&ilbd_rule_hlist, irl);
384
385 /*
386 * When dissociating a rule, only two errors can happen. The hc
387 * name is incorrect or the rule is not associated with the hc
388 * object. Both should not happen.... The check is for debugging
389 * purpose.
390 */
391 if (RULE_HAS_HC(irl) && (rc = ilbd_hc_dissociate_rule(irl)) !=
392 ILB_STATUS_OK) {
393 logerr("ilbd_destroy_one_rule: cannot "
394 "dissociate %s from hc object %s: %d",
395 irl->irl_name, irl->irl_hcname, rc);
396 }
397
398 rc = i_ilbd_save_rule(irl, ILBD_SCF_DESTROY);
399 if (rc != ILB_STATUS_OK)
400 logdebug("ilbd_destroy_rule: save rule failed");
401
402 free(irl);
403 return (rc);
404 }
405
406 /*
407 * the following two functions are the other's opposite, and can
408 * call into each other for roll back purposes in case of error.
409 * To avoid endless recursion, the 'is_rollback' parameter must be
410 * set to B_TRUE in the roll back case.
411 */
412 static ilb_status_t
ilbd_enable_one_rule(ilbd_rule_t * irl,boolean_t is_rollback)413 ilbd_enable_one_rule(ilbd_rule_t *irl, boolean_t is_rollback)
414 {
415 ilb_status_t rc = ILB_STATUS_OK;
416 ilb_name_cmd_t kcmd;
417
418 /* no use sending a no-op to the kernel */
419 if ((irl->irl_flags & ILB_FLAGS_RULE_ENABLED) != 0)
420 return (ILB_STATUS_OK);
421
422 irl->irl_flags |= ILB_FLAGS_RULE_ENABLED;
423
424 /* "all rules" is handled in one go somewhere else, not here */
425 if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
426 kcmd.cmd = ILB_ENABLE_RULE;
427 (void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
428 kcmd.flags = 0;
429
430 rc = do_ioctl(&kcmd, 0);
431 if (rc != ILB_STATUS_OK)
432 return (rc);
433 }
434 if (RULE_HAS_HC(irl) && (rc = ilbd_hc_enable_rule(irl)) !=
435 ILB_STATUS_OK) {
436 /* Undo the kernel work */
437 kcmd.cmd = ILB_DISABLE_RULE;
438 /* Cannot do much if ioctl fails... */
439 (void) do_ioctl(&kcmd, 0);
440 return (rc);
441 }
442
443 if (!is_rollback) {
444 if (rc == ILB_STATUS_OK)
445 rc = i_ilbd_save_rule(irl, ILBD_SCF_ENABLE_DISABLE);
446 if (rc != ILB_STATUS_OK)
447 /* ignore rollback return code */
448 (void) ilbd_disable_one_rule(irl, B_TRUE);
449 }
450
451 return (rc);
452 }
453
454 static ilb_status_t
ilbd_disable_one_rule(ilbd_rule_t * irl,boolean_t is_rollback)455 ilbd_disable_one_rule(ilbd_rule_t *irl, boolean_t is_rollback)
456 {
457 ilb_status_t rc = ILB_STATUS_OK;
458 ilb_name_cmd_t kcmd;
459
460 /* no use sending a no-op to the kernel */
461 if ((irl->irl_flags & ILB_FLAGS_RULE_ENABLED) == 0)
462 return (ILB_STATUS_OK);
463
464 irl->irl_flags &= ~ILB_FLAGS_RULE_ENABLED;
465
466 /* "all rules" is handled in one go somewhere else, not here */
467 if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
468 kcmd.cmd = ILB_DISABLE_RULE;
469 (void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
470 kcmd.flags = 0;
471
472 rc = do_ioctl(&kcmd, 0);
473 if (rc != ILB_STATUS_OK)
474 return (rc);
475 }
476
477 if (RULE_HAS_HC(irl) && (rc = ilbd_hc_disable_rule(irl)) !=
478 ILB_STATUS_OK) {
479 /* Undo the kernel work */
480 kcmd.cmd = ILB_ENABLE_RULE;
481 /* Cannot do much if ioctl fails... */
482 (void) do_ioctl(&kcmd, 0);
483 return (rc);
484 }
485
486 if (!is_rollback) {
487 if (rc == ILB_STATUS_OK)
488 rc = i_ilbd_save_rule(irl, ILBD_SCF_ENABLE_DISABLE);
489 if (rc != ILB_STATUS_OK)
490 /* ignore rollback return code */
491 (void) ilbd_enable_one_rule(irl, B_TRUE);
492 }
493
494 return (rc);
495 }
496
497 /*
498 * Generates an audit record for a supplied rule name
499 * Used for enable_rule, disable_rule, delete_rule,
500 * and create_rule subcommands
501 */
502 static void
ilbd_audit_rule_event(const char * audit_rule_name,ilb_rule_info_t * rlinfo,ilbd_cmd_t cmd,ilb_status_t rc,ucred_t * ucredp)503 ilbd_audit_rule_event(const char *audit_rule_name,
504 ilb_rule_info_t *rlinfo, ilbd_cmd_t cmd, ilb_status_t rc,
505 ucred_t *ucredp)
506 {
507 adt_session_data_t *ah;
508 adt_event_data_t *event;
509 au_event_t flag;
510 int scf_val_len = ILBD_MAX_VALUE_LEN;
511 char *aobuf = NULL; /* algo:topo */
512 char *valstr1 = NULL;
513 char *valstr2 = NULL;
514 char pbuf[PROTOCOL_LEN]; /* protocol */
515 char hcpbuf[PORT_LEN]; /* hcport */
516 int audit_error;
517
518 if ((ucredp == NULL) && (cmd == ILBD_CREATE_RULE)) {
519 /*
520 * we came here from the path where ilbd incorporates
521 * the configuration that is listed in SCF :
522 * i_ilbd_read_config->ilbd_walk_rule_pgs->
523 * ->ilbd_scf_instance_walk_pg->ilbd_create_rule
524 * We skip auditing in that case
525 */
526 return;
527 }
528 if (adt_start_session(&ah, NULL, 0) != 0) {
529 logerr("ilbd_audit_rule_event: adt_start_session failed");
530 exit(EXIT_FAILURE);
531 }
532 if (adt_set_from_ucred(ah, ucredp, ADT_NEW) != 0) {
533 (void) adt_end_session(ah);
534 logerr("ilbd_audit_rule_event: adt_set_from_ucred failed");
535 exit(EXIT_FAILURE);
536 }
537 if (cmd == ILBD_ENABLE_RULE)
538 flag = ADT_ilb_enable_rule;
539 else if (cmd == ILBD_DISABLE_RULE)
540 flag = ADT_ilb_disable_rule;
541 else if (cmd == ILBD_DESTROY_RULE)
542 flag = ADT_ilb_delete_rule;
543 else if (cmd == ILBD_CREATE_RULE)
544 flag = ADT_ilb_create_rule;
545
546 if ((event = adt_alloc_event(ah, flag)) == NULL) {
547 logerr("ilbd_audit_rule_event: adt_alloc_event failed");
548 exit(EXIT_FAILURE);
549 }
550
551 (void) memset((char *)event, 0, sizeof (adt_event_data_t));
552
553 switch (cmd) {
554 case ILBD_DESTROY_RULE:
555 event->adt_ilb_delete_rule.auth_used = NET_ILB_CONFIG_AUTH;
556 event->adt_ilb_delete_rule.rule_name = (char *)audit_rule_name;
557 break;
558 case ILBD_ENABLE_RULE:
559 event->adt_ilb_enable_rule.auth_used = NET_ILB_ENABLE_AUTH;
560 event->adt_ilb_enable_rule.rule_name = (char *)audit_rule_name;
561 break;
562 case ILBD_DISABLE_RULE:
563 event->adt_ilb_disable_rule.auth_used = NET_ILB_ENABLE_AUTH;
564 event->adt_ilb_disable_rule.rule_name = (char *)audit_rule_name;
565 break;
566 case ILBD_CREATE_RULE:
567 if (((aobuf = malloc(scf_val_len)) == NULL) ||
568 ((valstr1 = malloc(scf_val_len)) == NULL) ||
569 ((valstr2 = malloc(scf_val_len)) == NULL)) {
570 logerr("ilbd_audit_rule_event: could not"
571 " allocate buffer");
572 exit(EXIT_FAILURE);
573 }
574
575 event->adt_ilb_create_rule.auth_used = NET_ILB_CONFIG_AUTH;
576
577 /* Fill in virtual IP address type */
578 if (IN6_IS_ADDR_V4MAPPED(&rlinfo->rl_vip)) {
579 event->adt_ilb_create_rule.virtual_ipaddress_type =
580 ADT_IPv4;
581 cvt_addr(event->adt_ilb_create_rule.virtual_ipaddress,
582 ADT_IPv4, rlinfo->rl_vip);
583 } else {
584 event->adt_ilb_create_rule.virtual_ipaddress_type =
585 ADT_IPv6;
586 cvt_addr(event->adt_ilb_create_rule.virtual_ipaddress,
587 ADT_IPv6, rlinfo->rl_vip);
588 }
589 /* Fill in port - could be a single value or a range */
590 event->adt_ilb_create_rule.min_port = ntohs(rlinfo->rl_minport);
591 if (ntohs(rlinfo->rl_maxport) > ntohs(rlinfo->rl_minport)) {
592 /* port range */
593 event->adt_ilb_create_rule.max_port =
594 ntohs(rlinfo->rl_maxport);
595 } else {
596 /* in audit record, max=min when single port */
597 event->adt_ilb_create_rule.max_port =
598 ntohs(rlinfo->rl_minport);
599 }
600
601 /*
602 * Fill in protocol - if user does not specify it,
603 * its TCP by default
604 */
605 if (rlinfo->rl_proto == IPPROTO_UDP)
606 (void) snprintf(pbuf, PROTOCOL_LEN, "UDP");
607 else
608 (void) snprintf(pbuf, PROTOCOL_LEN, "TCP");
609 event->adt_ilb_create_rule.protocol = pbuf;
610
611 /* Fill in algorithm and operation type */
612 ilbd_algo_to_str(rlinfo->rl_algo, valstr1);
613 ilbd_topo_to_str(rlinfo->rl_topo, valstr2);
614 (void) snprintf(aobuf, scf_val_len, "%s:%s",
615 valstr1, valstr2);
616 event->adt_ilb_create_rule.algo_optype = aobuf;
617
618 /* Fill in proxy-src for the NAT case */
619 if (rlinfo->rl_topo == ILB_TOPO_NAT) {
620 /* copy starting proxy-src address */
621 if (IN6_IS_ADDR_V4MAPPED(&rlinfo->rl_nat_src_start)) {
622 /* V4 case */
623 event->adt_ilb_create_rule.proxy_src_min_type =
624 ADT_IPv4;
625 cvt_addr(
626 event->adt_ilb_create_rule.proxy_src_min,
627 ADT_IPv4, rlinfo->rl_nat_src_start);
628 } else {
629 /* V6 case */
630 event->adt_ilb_create_rule.proxy_src_min_type =
631 ADT_IPv6;
632 cvt_addr(
633 event->adt_ilb_create_rule.proxy_src_min,
634 ADT_IPv6, rlinfo->rl_nat_src_start);
635 }
636
637 /* copy ending proxy-src address */
638 if (IN6_IS_ADDR_UNSPECIFIED(&rlinfo->rl_nat_src_end)) {
639 /* proxy-src is a single address */
640 event->adt_ilb_create_rule.proxy_src_max_type =
641 event->
642 adt_ilb_create_rule.proxy_src_min_type;
643 (void) memcpy(
644 event->adt_ilb_create_rule.proxy_src_max,
645 event->adt_ilb_create_rule.proxy_src_min,
646 (4 * sizeof (uint32_t)));
647 } else if (
648 IN6_IS_ADDR_V4MAPPED(&rlinfo->rl_nat_src_end)) {
649 /*
650 * proxy-src is a address range - copy ending
651 * proxy-src address
652 * V4 case
653 */
654 event->adt_ilb_create_rule.proxy_src_max_type =
655 ADT_IPv4;
656 cvt_addr(
657 event->adt_ilb_create_rule.proxy_src_max,
658 ADT_IPv4, rlinfo->rl_nat_src_end);
659 } else {
660 /* V6 case */
661 event->adt_ilb_create_rule.proxy_src_max_type =
662 ADT_IPv6;
663 cvt_addr(
664 event->adt_ilb_create_rule.proxy_src_max,
665 ADT_IPv6, rlinfo->rl_nat_src_end);
666 }
667 }
668
669 /*
670 * Fill in pmask if user has specified one - 0 means
671 * no persistence
672 */
673 valstr1[0] = '\0';
674 ilbd_ip_to_str(rlinfo->rl_ipversion, &rlinfo->rl_stickymask,
675 valstr1);
676 event->adt_ilb_create_rule.persist_mask = valstr1;
677
678 /* If there is a hcname */
679 if (rlinfo->rl_hcname[0] != '\0')
680 event->adt_ilb_create_rule.hcname = rlinfo->rl_hcname;
681
682 /* Fill in hcport */
683 if (rlinfo->rl_hcpflag == ILB_HCI_PROBE_FIX) {
684 /* hcport is specified by user */
685 (void) snprintf(hcpbuf, PORT_LEN, "%d",
686 rlinfo->rl_hcport);
687 event->adt_ilb_create_rule.hcport = hcpbuf;
688 } else if (rlinfo->rl_hcpflag == ILB_HCI_PROBE_ANY) {
689 /* user has specified "ANY" */
690 (void) snprintf(hcpbuf, PORT_LEN, "ANY");
691 event->adt_ilb_create_rule.hcport = hcpbuf;
692 }
693 /*
694 * Fill out the conndrain, nat_timeout and persist_timeout
695 * If the user does not specify them, the default value
696 * is set in the kernel. Userland does not know what
697 * the values are. So if the user
698 * does not specify these values they will show up as
699 * 0 in the audit record.
700 */
701 event->adt_ilb_create_rule.conndrain_timeout =
702 rlinfo->rl_conndrain;
703 event->adt_ilb_create_rule.nat_timeout =
704 rlinfo->rl_nat_timeout;
705 event->adt_ilb_create_rule.persist_timeout =
706 rlinfo->rl_sticky_timeout;
707
708 /* Fill out servergroup and rule name */
709 event->adt_ilb_create_rule.server_group = rlinfo->rl_sgname;
710 event->adt_ilb_create_rule.rule_name = rlinfo->rl_name;
711 break;
712 }
713 if (rc == ILB_STATUS_OK) {
714 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
715 logerr("ilbd_audit_rule_event:adt_put_event failed");
716 exit(EXIT_FAILURE);
717 }
718 } else {
719 audit_error = ilberror2auditerror(rc);
720 if (adt_put_event(event, ADT_FAILURE, audit_error) != 0) {
721 logerr("ilbd_audit_rule_event: adt_put_event failed");
722 exit(EXIT_FAILURE);
723 }
724 }
725 adt_free_event(event);
726 free(aobuf);
727 free(valstr1);
728 free(valstr2);
729 (void) adt_end_session(ah);
730 }
731 /*
732 * converts IP address from in6_addr format to uint32_t[4]
733 * This conversion is needed for recording IP address in
734 * audit records.
735 */
736 void
cvt_addr(uint32_t * audit,int32_t type,struct in6_addr address)737 cvt_addr(uint32_t *audit, int32_t type, struct in6_addr address)
738 {
739
740 if (type == ADT_IPv4) {
741 /* address is IPv4 */
742 audit[0] = address._S6_un._S6_u32[3];
743 } else {
744 /* address is IPv6 */
745 (void) memcpy(audit, address._S6_un._S6_u32,
746 (4 * sizeof (uint32_t)));
747 }
748 }
749
750 static ilb_status_t
i_ilbd_action_switch(ilbd_rule_t * irl,ilbd_cmd_t cmd,boolean_t is_rollback,ucred_t * ucredp)751 i_ilbd_action_switch(ilbd_rule_t *irl, ilbd_cmd_t cmd,
752 boolean_t is_rollback, ucred_t *ucredp)
753 {
754 ilb_status_t rc;
755
756 switch (cmd) {
757 case ILBD_DESTROY_RULE:
758 rc = ilbd_destroy_one_rule(irl);
759 if (!is_rollback) {
760 ilbd_audit_rule_event(irl->irl_name, NULL,
761 cmd, rc, ucredp);
762 }
763 return (rc);
764 case ILBD_ENABLE_RULE:
765 rc = ilbd_enable_one_rule(irl, is_rollback);
766 if (!is_rollback) {
767 ilbd_audit_rule_event(irl->irl_name, NULL, cmd,
768 rc, ucredp);
769 }
770 return (rc);
771 case ILBD_DISABLE_RULE:
772 rc = ilbd_disable_one_rule(irl, is_rollback);
773 if (!is_rollback) {
774 ilbd_audit_rule_event(irl->irl_name, NULL, cmd,
775 rc, ucredp);
776 }
777 return (rc);
778 }
779 return (ILB_STATUS_INVAL_CMD);
780 }
781
782 static ilb_cmd_t
i_ilbd2ilb_cmd(ilbd_cmd_t c)783 i_ilbd2ilb_cmd(ilbd_cmd_t c)
784 {
785 ilb_cmd_t r;
786
787 switch (c) {
788 case ILBD_CREATE_RULE:
789 r = ILB_CREATE_RULE;
790 break;
791 case ILBD_DESTROY_RULE:
792 r = ILB_DESTROY_RULE;
793 break;
794 case ILBD_ENABLE_RULE:
795 r = ILB_ENABLE_RULE;
796 break;
797 case ILBD_DISABLE_RULE:
798 r = ILB_DISABLE_RULE;
799 break;
800 }
801 return (r);
802 }
803
804 static ilbd_cmd_t
get_undo_cmd(ilbd_cmd_t cmd)805 get_undo_cmd(ilbd_cmd_t cmd)
806 {
807 ilbd_cmd_t u_cmd;
808
809 switch (cmd) {
810 case ILBD_DESTROY_RULE:
811 u_cmd = ILBD_BAD_CMD;
812 break;
813 case ILBD_ENABLE_RULE:
814 u_cmd = ILBD_DISABLE_RULE;
815 break;
816 case ILBD_DISABLE_RULE:
817 u_cmd = ILBD_ENABLE_RULE;
818 break;
819 }
820
821 return (u_cmd);
822 }
823
824 static ilb_status_t
i_ilbd_rule_action(const char * rule_name,const struct passwd * ps,ilbd_cmd_t cmd,ucred_t * ucredp)825 i_ilbd_rule_action(const char *rule_name, const struct passwd *ps,
826 ilbd_cmd_t cmd, ucred_t *ucredp)
827 {
828 ilbd_rule_t *irl, *irl_next;
829 boolean_t is_all_rules = B_FALSE;
830 ilb_status_t rc = ILB_STATUS_OK;
831 ilb_name_cmd_t kcmd;
832 ilbd_cmd_t u_cmd;
833 char rulename[ILB_NAMESZ];
834
835 if (ps != NULL) {
836 if ((cmd == ILBD_ENABLE_RULE) || (cmd == ILBD_DISABLE_RULE))
837 rc = ilbd_check_client_enable_auth(ps);
838 else
839 rc = ilbd_check_client_config_auth(ps);
840 /* generate the audit record before bailing out */
841 if (rc != ILB_STATUS_OK) {
842 if (*rule_name != '\0') {
843 ilbd_audit_rule_event(rule_name, NULL,
844 cmd, rc, ucredp);
845 } else {
846 (void) snprintf(rulename, sizeof (rulename),
847 "all");
848 ilbd_audit_rule_event(rulename, NULL, cmd, rc,
849 ucredp);
850 }
851 goto out;
852 }
853 }
854 is_all_rules = rule_name[0] == 0;
855
856 /* just one rule */
857 if (!is_all_rules) {
858 irl = i_find_rule_byname(rule_name);
859 if (irl == NULL) {
860 rc = ILB_STATUS_ENORULE;
861 ilbd_audit_rule_event(rule_name, NULL, cmd, rc, ucredp);
862 goto out;
863 }
864 /* auditing will be done by i_ilbd_action_switch() */
865 rc = i_ilbd_action_switch(irl, cmd, B_FALSE, ucredp);
866 goto out;
867 }
868
869 /* all rules: first tell the kernel, then walk the daemon's list */
870 kcmd.cmd = i_ilbd2ilb_cmd(cmd);
871 kcmd.flags = ILB_RULE_ALLRULES;
872
873 rc = do_ioctl(&kcmd, 0);
874 if (rc != ILB_STATUS_OK) {
875 (void) snprintf(rulename, sizeof (rulename), "all");
876 ilbd_audit_rule_event(rulename, NULL, cmd, rc, ucredp);
877 goto out;
878 }
879
880 irl = list_head(&ilbd_rule_hlist);
881 while (irl != NULL) {
882 irl_next = list_next(&ilbd_rule_hlist, irl);
883 irl->irl_flags |= ILB_FLAGS_RULE_ALLRULES;
884 /* auditing will be done by i_ilbd_action_switch() */
885 rc = i_ilbd_action_switch(irl, cmd, B_FALSE, ucredp);
886 irl->irl_flags &= ~ILB_FLAGS_RULE_ALLRULES;
887 if (rc != ILB_STATUS_OK)
888 goto rollback_list;
889 irl = irl_next;
890 }
891 return (rc);
892
893 rollback_list:
894 u_cmd = get_undo_cmd(cmd);
895 if (u_cmd == ILBD_BAD_CMD)
896 return (rc);
897
898 if (is_all_rules) {
899 kcmd.cmd = i_ilbd2ilb_cmd(u_cmd);
900 (void) do_ioctl(&kcmd, 0);
901 }
902 /* current list element failed, so we start with previous one */
903 irl = list_prev(&ilbd_rule_hlist, irl);
904 while (irl != NULL) {
905 if (is_all_rules)
906 irl->irl_flags |= ILB_FLAGS_RULE_ALLRULES;
907
908 /*
909 * When the processing of a command consists of
910 * multiple sequential steps, and one of them fails,
911 * ilbd performs rollback to undo the steps taken before the
912 * failing step. Since ilbd is initiating these steps
913 * there is not need to audit them.
914 */
915 rc = i_ilbd_action_switch(irl, u_cmd, B_TRUE, NULL);
916 irl->irl_flags &= ~ILB_FLAGS_RULE_ALLRULES;
917
918 irl = list_prev(&ilbd_rule_hlist, irl);
919 }
920 out:
921 return (rc);
922 }
923
924 ilb_status_t
ilbd_destroy_rule(ilbd_name_t rule_name,const struct passwd * ps,ucred_t * ucredp)925 ilbd_destroy_rule(ilbd_name_t rule_name, const struct passwd *ps,
926 ucred_t *ucredp)
927 {
928 return (i_ilbd_rule_action(rule_name, ps, ILBD_DESTROY_RULE, ucredp));
929 }
930
931 ilb_status_t
ilbd_enable_rule(ilbd_name_t rule_name,const struct passwd * ps,ucred_t * ucredp)932 ilbd_enable_rule(ilbd_name_t rule_name, const struct passwd *ps,
933 ucred_t *ucredp)
934 {
935 return (i_ilbd_rule_action(rule_name, ps, ILBD_ENABLE_RULE, ucredp));
936
937 }
938
939 ilb_status_t
ilbd_disable_rule(ilbd_name_t rule_name,const struct passwd * ps,ucred_t * ucredp)940 ilbd_disable_rule(ilbd_name_t rule_name, const struct passwd *ps,
941 ucred_t *ucredp)
942 {
943 return (i_ilbd_rule_action(rule_name, ps, ILBD_DISABLE_RULE, ucredp));
944 }
945
946 /*
947 * allocate storage for a kernel rule command and fill from
948 * "template" irl, if non-NULL
949 */
950 static ilb_rule_cmd_t *
i_alloc_kernel_rule_cmd(ilbd_rule_t * irl)951 i_alloc_kernel_rule_cmd(ilbd_rule_t *irl)
952 {
953 ilb_rule_cmd_t *kcmd;
954
955 kcmd = (ilb_rule_cmd_t *)malloc(sizeof (*kcmd));
956 if (kcmd == NULL)
957 return (kcmd);
958
959 bzero(kcmd, sizeof (*kcmd));
960
961 if (irl != NULL) {
962 kcmd->flags = irl->irl_flags;
963 kcmd->ip_ver = AF_2_IPPROTO(irl->irl_ipversion);
964 kcmd->vip = irl->irl_vip;
965 kcmd->proto = irl->irl_proto;
966 kcmd->min_port = irl->irl_minport;
967 kcmd->max_port = irl->irl_maxport;
968 kcmd->algo = algo_lib2impl(irl->irl_algo);
969 kcmd->topo = topo_lib2impl(irl->irl_topo);
970 kcmd->sticky_mask = irl->irl_stickymask;
971 kcmd->nat_src_start = irl->irl_nat_src_start;
972 kcmd->nat_src_end = irl->irl_nat_src_end;
973 kcmd->conn_drain_timeout = irl->irl_conndrain;
974 kcmd->nat_expiry = irl->irl_nat_timeout;
975 kcmd->sticky_expiry = irl->irl_sticky_timeout;
976 (void) strlcpy(kcmd->name, irl->irl_name,
977 sizeof (kcmd->name));
978 }
979 return (kcmd);
980 }
981
982 /*
983 * ncount is the next to be used index into (*kcmdp)->servers
984 */
985 static ilb_status_t
adjust_srv_info_cmd(ilb_servers_info_cmd_t ** kcmdp,int index)986 adjust_srv_info_cmd(ilb_servers_info_cmd_t **kcmdp, int index)
987 {
988 ilb_servers_info_cmd_t *kcmd = *kcmdp;
989 size_t sz;
990
991 if (kcmd != NULL && kcmd->num_servers > index + 1)
992 return (ILB_STATUS_OK);
993
994 /*
995 * the first ilb_server_info_t is part of *kcmd, so
996 * by using index (which is one less than the total needed) here,
997 * we allocate exactly the amount we need.
998 */
999 sz = sizeof (*kcmd) + (index * sizeof (ilb_server_info_t));
1000 kcmd = (ilb_servers_info_cmd_t *)realloc(kcmd, sz);
1001 if (kcmd == NULL)
1002 return (ILB_STATUS_ENOMEM);
1003
1004 /*
1005 * we don't count the slot we newly allocated yet.
1006 */
1007 kcmd->num_servers = index;
1008 *kcmdp = kcmd;
1009
1010 return (ILB_STATUS_OK);
1011 }
1012
1013 /*
1014 * this function adds all servers in srvlist to the kernel(!) rule
1015 * the name of which is passed as argument.
1016 */
1017 static ilb_status_t
i_update_ksrv_rules(char * name,ilbd_sg_t * sg,ilbd_rule_t * rl)1018 i_update_ksrv_rules(char *name, ilbd_sg_t *sg, ilbd_rule_t *rl)
1019 {
1020 ilb_status_t rc;
1021 ilbd_srv_t *srvp;
1022 ilb_servers_info_cmd_t *kcmd = NULL;
1023 int i;
1024
1025 /*
1026 * If the servergroup doesn't have any servers associated with
1027 * it yet, there's nothing more to do here.
1028 */
1029 if (sg->isg_srvcount == 0)
1030 return (ILB_STATUS_OK);
1031
1032 /*
1033 * walk the list of servers attached to this SG
1034 */
1035 srvp = list_head(&sg->isg_srvlist);
1036 for (i = 0; srvp != NULL; srvp = list_next(&sg->isg_srvlist, srvp)) {
1037 rc = adjust_srv_info_cmd(&kcmd, i);
1038 if (rc != ILB_STATUS_OK)
1039 goto rollback_kcmd;
1040
1041 ILB_SGSRV_2_KSRV(&srvp->isv_srv, &kcmd->servers[i]);
1042 /*
1043 * "no port" means "copy rule's port" (for kernel rule)
1044 */
1045 if (kcmd->servers[i].min_port == 0) {
1046 kcmd->servers[i].min_port = rl->irl_minport;
1047 kcmd->servers[i].max_port = rl->irl_maxport;
1048 }
1049 i++;
1050 }
1051 assert(kcmd != NULL);
1052
1053 kcmd->cmd = ILB_ADD_SERVERS;
1054 kcmd->num_servers = i;
1055 (void) strlcpy(kcmd->name, name, sizeof (kcmd->name));
1056
1057 rc = do_ioctl(kcmd, 0);
1058 if (rc != ILB_STATUS_OK)
1059 goto rollback_kcmd;
1060
1061 for (i = 0; i < kcmd->num_servers; i++) {
1062 int e;
1063
1064 if ((e = kcmd->servers[i].err) != 0) {
1065 logerr("i_update_ksrv_rules "
1066 "ioctl indicates failure: %s", strerror(e));
1067 rc = ilb_map_errno2ilbstat(e);
1068 /*
1069 * if adding even a single server failed, we need to
1070 * roll back the whole wad. We ignore any errors and
1071 * return the one that was returned by the first ioctl.
1072 */
1073 kcmd->cmd = ILB_DEL_SERVERS;
1074 (void) do_ioctl(kcmd, 0);
1075 goto rollback_kcmd;
1076 }
1077 }
1078
1079 rollback_kcmd:
1080 free(kcmd);
1081 return (rc);
1082 }
1083
1084 /* convert a struct in6_addr to valstr */
1085 void
ilbd_ip_to_str(uint16_t ipversion,struct in6_addr * addr,char * valstr)1086 ilbd_ip_to_str(uint16_t ipversion, struct in6_addr *addr, char *valstr)
1087 {
1088 size_t vallen;
1089 ilb_ip_addr_t ipaddr;
1090 void *addrptr;
1091
1092 vallen = (ipversion == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1093
1094 IP_COPY_IMPL_2_CLI(addr, &ipaddr);
1095 addrptr = (ipversion == AF_INET) ?
1096 (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
1097 if (inet_ntop(ipversion, (void *)addrptr, valstr, vallen) == NULL)
1098 logerr("ilbd_ip_to_str: inet_ntop failed");
1099 return;
1100
1101 }
1102
1103 ilb_status_t
ilbd_create_rule(ilb_rule_info_t * rl,int ev_port,const struct passwd * ps,ucred_t * ucredp)1104 ilbd_create_rule(ilb_rule_info_t *rl, int ev_port,
1105 const struct passwd *ps, ucred_t *ucredp)
1106 {
1107 ilb_status_t rc;
1108 ilbd_rule_t *irl = NULL;
1109 ilbd_sg_t *sg;
1110 ilb_rule_cmd_t *kcmd = NULL;
1111
1112 if (ps != NULL) {
1113 if ((rc = ilbd_check_client_config_auth(ps)) != ILB_STATUS_OK)
1114 goto out;
1115 }
1116
1117 if (i_find_rule_byname(rl->rl_name) != NULL) {
1118 logdebug("ilbd_create_rule: rule %s"
1119 " already exists", rl->rl_name);
1120 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1121 ILB_STATUS_DUP_RULE, ucredp);
1122 return (ILB_STATUS_DUP_RULE);
1123 }
1124
1125 sg = i_find_sg_byname(rl->rl_sgname);
1126 if (sg == NULL) {
1127 logdebug("ilbd_create_rule: rule %s uses non-existent"
1128 " servergroup name %s", rl->rl_name, rl->rl_sgname);
1129 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1130 ILB_STATUS_SGUNAVAIL, ucredp);
1131 return (ILB_STATUS_SGUNAVAIL);
1132 }
1133
1134 if ((rc = ilbd_sg_check_rule_port(sg, rl)) != ILB_STATUS_OK) {
1135 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1136 return (rc);
1137 }
1138
1139 /* allocs and copies contents of arg (if != NULL) into new rule */
1140 irl = i_alloc_ilbd_rule(rl);
1141 if (irl == NULL) {
1142 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1143 ILB_STATUS_ENOMEM, ucredp);
1144 return (ILB_STATUS_ENOMEM);
1145 }
1146
1147 /* make sure rule's IPversion (via vip) and SG's match */
1148 if (sg->isg_srvcount > 0) {
1149 ilbd_srv_t *srv = list_head(&sg->isg_srvlist);
1150 int32_t r_af = rl->rl_ipversion;
1151 int32_t s_af = GET_AF(&srv->isv_addr);
1152
1153 if (r_af != s_af) {
1154 logdebug("address family mismatch with servergroup");
1155 rc = ILB_STATUS_MISMATCHSG;
1156 goto out;
1157 }
1158 }
1159 irl->irl_sg = sg;
1160
1161 /* Try associating the rule with the given hc oject. */
1162 if (RULE_HAS_HC(irl)) {
1163 if ((rc = ilbd_hc_associate_rule(irl, ev_port)) !=
1164 ILB_STATUS_OK)
1165 goto out;
1166 }
1167
1168 /*
1169 * checks are done, now:
1170 * 1. create rule in kernel
1171 * 2. tell it about the backend server (which we maintain in SG)
1172 * 3. attach the rule in memory
1173 */
1174 /* 1. */
1175 /* allocs and copies contents of arg (if != NULL) into new rule */
1176 kcmd = i_alloc_kernel_rule_cmd(irl);
1177 if (kcmd == NULL) {
1178 rc = ILB_STATUS_ENOMEM;
1179 goto rollback_hc;
1180 }
1181 kcmd->cmd = ILB_CREATE_RULE;
1182
1183 rc = do_ioctl(kcmd, 0);
1184 if (rc != ILB_STATUS_OK)
1185 goto rollback_kcmd;
1186
1187 /* 2. */
1188 rc = i_update_ksrv_rules(kcmd->name, sg, irl);
1189 if (rc != ILB_STATUS_OK)
1190 goto rollback_kcmd;
1191
1192 /* 3. */
1193 (void) i_attach_rule2sg(sg, irl);
1194 list_insert_tail(&ilbd_rule_hlist, irl);
1195
1196 if (ps != NULL) {
1197 rc = i_ilbd_save_rule(irl, ILBD_SCF_CREATE);
1198 if (rc != ILB_STATUS_OK)
1199 goto rollback_rule;
1200 }
1201
1202 free(kcmd);
1203 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1204 ILB_STATUS_OK, ucredp);
1205 return (ILB_STATUS_OK);
1206
1207 rollback_rule:
1208 /*
1209 * ilbd_destroy_one_rule() also frees irl, as well as dissociate
1210 * rule and HC, so all we need to do afterwards is free the kcmd
1211 * and return.
1212 */
1213 (void) ilbd_destroy_one_rule(irl);
1214 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1215 free(kcmd);
1216 return (rc);
1217
1218 rollback_kcmd:
1219 free(kcmd);
1220 rollback_hc:
1221 /* Cannot fail since the rule is just associated with the hc object. */
1222 if (RULE_HAS_HC(irl))
1223 (void) ilbd_hc_dissociate_rule(irl);
1224 out:
1225 ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1226 free(irl);
1227 return (rc);
1228 }
1229
1230 static uint32_t
i_flags_d2k(int f)1231 i_flags_d2k(int f)
1232 {
1233 uint32_t r = 0;
1234
1235 if (ILB_IS_SRV_ENABLED(f))
1236 r |= ILB_SERVER_ENABLED;
1237 /* more as they are defined */
1238
1239 return (r);
1240 }
1241
1242 /*
1243 * walk the list of rules and add srv to the *kernel* rule
1244 * (this is a list of rules hanging off of a server group)
1245 */
1246 ilb_status_t
i_add_srv2krules(list_t * rlist,ilb_sg_srv_t * srv,int ev_port)1247 i_add_srv2krules(list_t *rlist, ilb_sg_srv_t *srv, int ev_port)
1248 {
1249 ilb_status_t rc = ILB_STATUS_OK;
1250 ilbd_rule_t *rl, *del_rl;
1251 ilb_servers_info_cmd_t kcmd;
1252 ilb_servers_cmd_t del_kcmd;
1253
1254 kcmd.cmd = ILB_ADD_SERVERS;
1255 kcmd.num_servers = 1;
1256 kcmd.servers[0].err = 0;
1257 kcmd.servers[0].addr = srv->sgs_addr;
1258 kcmd.servers[0].flags = i_flags_d2k(srv->sgs_flags);
1259 (void) strlcpy(kcmd.servers[0].name, srv->sgs_srvID,
1260 sizeof (kcmd.servers[0].name));
1261
1262 /*
1263 * a note about rollback: since we need to start rollback with the
1264 * current list element in some case, and with the previous one
1265 * in others, we must "go back" in this latter case before
1266 * we jump to the rollback code.
1267 */
1268 for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
1269 (void) strlcpy(kcmd.name, rl->irl_name, sizeof (kcmd.name));
1270 /*
1271 * sgs_minport == 0 means "no port specified"; this
1272 * indicates that the server matches anything the rule
1273 * provides.
1274 * NOTE: this can be different for different rules
1275 * using the same server group, therefore we don't modify
1276 * this information in the servergroup, but *only* in
1277 * the kernel's rule.
1278 */
1279 if (srv->sgs_minport == 0) {
1280 kcmd.servers[0].min_port = rl->irl_minport;
1281 kcmd.servers[0].max_port = rl->irl_maxport;
1282 } else {
1283 kcmd.servers[0].min_port = srv->sgs_minport;
1284 kcmd.servers[0].max_port = srv->sgs_maxport;
1285 }
1286 rc = do_ioctl((void *)&kcmd, 0);
1287 if (rc != ILB_STATUS_OK) {
1288 logdebug("i_add_srv2krules: do_ioctl call failed");
1289 del_rl = list_prev(rlist, rl);
1290 goto rollback;
1291 }
1292
1293 /*
1294 * if ioctl() returns != 0, it doesn't perform the copyout
1295 * necessary to indicate *which* server failed (we could be
1296 * adding more than one); therefore we must check this
1297 * 'err' field even if ioctl() returns 0.
1298 */
1299 if (kcmd.servers[0].err != 0) {
1300 logerr("i_add_srv2krules: SIOCILB ioctl returned"
1301 " error %d", kcmd.servers[0].err);
1302 rc = ilb_map_errno2ilbstat(kcmd.servers[0].err);
1303 del_rl = list_prev(rlist, rl);
1304 goto rollback;
1305 }
1306 if (RULE_HAS_HC(rl)) {
1307 if ((rc = ilbd_hc_add_server(rl, srv, ev_port)) !=
1308 ILB_STATUS_OK) {
1309 logerr("i_add_srv2krules: cannot start timer "
1310 " for rules %s server %s", rl->irl_name,
1311 srv->sgs_srvID);
1312
1313 del_rl = rl;
1314 goto rollback;
1315 }
1316 }
1317 }
1318
1319 return (rc);
1320
1321 rollback:
1322 /*
1323 * this is almost, but not quite, the same as i_rem_srv_frm_krules()
1324 * therefore we keep it seperate.
1325 */
1326 del_kcmd.cmd = ILB_DEL_SERVERS;
1327 del_kcmd.num_servers = 1;
1328 del_kcmd.servers[0].addr = srv->sgs_addr;
1329 while (del_rl != NULL) {
1330 if (RULE_HAS_HC(del_rl))
1331 (void) ilbd_hc_del_server(del_rl, srv);
1332 (void) strlcpy(del_kcmd.name, del_rl->irl_name,
1333 sizeof (del_kcmd.name));
1334 (void) do_ioctl((void *)&del_kcmd, 0);
1335 del_rl = list_prev(rlist, del_rl);
1336 }
1337
1338 return (rc);
1339 }
1340
1341 /*
1342 * ev_port is only used for rollback purposes in this function
1343 */
1344 ilb_status_t
i_rem_srv_frm_krules(list_t * rlist,ilb_sg_srv_t * srv,int ev_port)1345 i_rem_srv_frm_krules(list_t *rlist, ilb_sg_srv_t *srv, int ev_port)
1346 {
1347 ilb_status_t rc = ILB_STATUS_OK;
1348 ilbd_rule_t *rl, *add_rl;
1349 ilb_servers_cmd_t kcmd;
1350 ilb_servers_info_cmd_t add_kcmd;
1351
1352 kcmd.cmd = ILB_DEL_SERVERS;
1353 kcmd.num_servers = 1;
1354 kcmd.servers[0].err = 0;
1355 kcmd.servers[0].addr = srv->sgs_addr;
1356
1357 for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
1358 (void) strlcpy(kcmd.name, rl->irl_name, sizeof (kcmd.name));
1359 rc = do_ioctl((void *)&kcmd, 0);
1360 if (rc != ILB_STATUS_OK) {
1361 logdebug("i_rem_srv_frm_krules: do_ioctl"
1362 "call failed");
1363 add_rl = list_prev(rlist, rl);
1364 goto rollback;
1365 }
1366 /*
1367 * if ioctl() returns != 0, it doesn't perform the copyout
1368 * necessary to indicate *which* server failed (we could be
1369 * removing more than one); therefore we must check this
1370 * 'err' field even if ioctl() returns 0.
1371 */
1372 if (kcmd.servers[0].err != 0) {
1373 logerr("i_rem_srv_frm_krules: SIOCILB ioctl"
1374 " returned error %s",
1375 strerror(kcmd.servers[0].err));
1376 rc = ilb_map_errno2ilbstat(kcmd.servers[0].err);
1377 add_rl = list_prev(rlist, rl);
1378 goto rollback;
1379 }
1380 if (RULE_HAS_HC(rl) &&
1381 (rc = ilbd_hc_del_server(rl, srv)) != ILB_STATUS_OK) {
1382 logerr("i_rem_srv_frm_krules: cannot delete "
1383 "timer for rules %s server %s", rl->irl_name,
1384 srv->sgs_srvID);
1385 add_rl = rl;
1386 goto rollback;
1387 }
1388 }
1389
1390 return (rc);
1391
1392 rollback:
1393 /* Don't do roll back if ev_port == -1. */
1394 if (ev_port == -1)
1395 return (rc);
1396
1397 add_kcmd.cmd = ILB_ADD_SERVERS;
1398 add_kcmd.num_servers = 1;
1399 add_kcmd.servers[0].err = 0;
1400 add_kcmd.servers[0].addr = srv->sgs_addr;
1401 add_kcmd.servers[0].flags = i_flags_d2k(srv->sgs_flags);
1402 (void) strlcpy(add_kcmd.servers[0].name, srv->sgs_srvID,
1403 sizeof (add_kcmd.servers[0].name));
1404 while (add_rl != NULL) {
1405 if (srv->sgs_minport == 0) {
1406 add_kcmd.servers[0].min_port = add_rl->irl_minport;
1407 add_kcmd.servers[0].max_port = add_rl->irl_maxport;
1408 } else {
1409 add_kcmd.servers[0].min_port = srv->sgs_minport;
1410 add_kcmd.servers[0].max_port = srv->sgs_maxport;
1411 }
1412 if (RULE_HAS_HC(add_rl))
1413 (void) ilbd_hc_add_server(add_rl, srv, ev_port);
1414 (void) strlcpy(add_kcmd.name, add_rl->irl_name,
1415 sizeof (add_kcmd.name));
1416 (void) do_ioctl((void *)&add_kcmd, 0);
1417 add_rl = list_prev(rlist, add_rl);
1418 }
1419
1420 return (rc);
1421 }
1422