1 /* $OpenBSD: net_ctl.c,v 1.11 2016/07/18 21:22:09 benno Exp $ */
2
3 /*
4 * Copyright (c) 2005 H�kan Olsson. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * This code was written under funding by Multicom Security AB.
30 */
31
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include "sasyncd.h"
45 #include "net.h"
46
47 struct ctlmsg {
48 u_int32_t type;
49 u_int32_t data;
50 u_int32_t data2;
51 };
52
53 int snapcount = 0;
54
55 static int
net_ctl_check_state(struct syncpeer * p,enum RUNSTATE nstate)56 net_ctl_check_state(struct syncpeer *p, enum RUNSTATE nstate)
57 {
58 if (nstate < INIT || nstate > FAIL) {
59 log_msg(0, "net_ctl: got bad state %d from peer \"%s\"",
60 nstate, p->name);
61 net_ctl_send_error(p, CTL_STATE);
62 return -1;
63 }
64 if (cfgstate.runstate == MASTER && nstate == MASTER) {
65 log_msg(0, "net_ctl: got bad state MASTER from peer \"%s\"",
66 p->name);
67 net_ctl_send_error(p, CTL_STATE);
68 return -1;
69 }
70 if (p->runstate != nstate) {
71 p->runstate = nstate;
72 log_msg(1, "net_ctl: peer \"%s\" state change to %s", p->name,
73 carp_state_name(nstate));
74 }
75 return 0;
76 }
77
78 void
net_ctl_handle_msg(struct syncpeer * p,u_int8_t * msg,u_int32_t msglen)79 net_ctl_handle_msg(struct syncpeer *p, u_int8_t *msg, u_int32_t msglen)
80 {
81 struct ctlmsg *ctl = (struct ctlmsg *)msg;
82 enum RUNSTATE nstate;
83 enum CTLTYPE ctype;
84 static char *ct, *ctltype[] = CTLTYPES;
85
86 if (msglen < sizeof *ctl) {
87 log_msg(0, "net_ctl: got bad control message from peer \"%s\"",
88 p->name);
89 net_ctl_send_error(p, CTL_UNKNOWN);
90 return;
91 }
92
93 switch (ntohl(ctl->type)) {
94 case CTL_ENDSNAP:
95 log_msg(2, "net_ctl: got CTL_ENDSNAP from peer \"%s\"",
96 p->name);
97
98 /* XXX More sophistication required to handle multiple peers. */
99 if (carp_demoted) {
100 snapcount++;
101 if (snapcount >= cfgstate.peercnt)
102 monitor_carpundemote(NULL);
103 }
104 break;
105
106 case CTL_STATE:
107 log_msg(2, "net_ctl: got CTL_STATE from peer \"%s\"", p->name);
108 nstate = (enum RUNSTATE)ntohl(ctl->data);
109 if (net_ctl_check_state(p, nstate) == 0)
110 net_ctl_send_ack(p, CTL_STATE, cfgstate.runstate);
111 break;
112
113 case CTL_ERROR:
114 log_msg(1, "net_ctl: got ERROR from peer \"%s\"", p->name);
115
116 switch (ntohl(ctl->data)) {
117 case RESERVED: /* PFKEY -- nothing to do here for now */
118 break;
119
120 case CTL_STATE:
121 nstate = cfgstate.runstate;
122 carp_check_state();
123 if (nstate != cfgstate.runstate)
124 net_ctl_send_state(p);
125 break;
126
127 case CTL_UNKNOWN:
128 default:
129 break;
130 }
131 break;
132
133 case CTL_ACK:
134 ctype = (enum CTLTYPE)ntohl(ctl->data);
135 if (ctype < RESERVED || ctype > CTL_UNKNOWN)
136 ct = "<unknown>";
137 else
138 ct = ctltype[ctype];
139 log_msg(2, "net_ctl: got %s ACK from peer \"%s\"", ct,
140 p->name);
141 if (ctype == CTL_STATE) {
142 nstate = (enum RUNSTATE)ntohl(ctl->data2);
143 net_ctl_check_state(p, nstate);
144 }
145 break;
146
147 case CTL_UNKNOWN:
148 default:
149 log_msg(1, "net_ctl: got unknown msg type %u from peer \"%s\"",
150 ntohl(ctl->type), p->name);
151 break;
152 }
153 }
154
155 static int
net_ctl_send(struct syncpeer * p,u_int32_t type,u_int32_t d,u_int32_t d2)156 net_ctl_send(struct syncpeer *p, u_int32_t type, u_int32_t d, u_int32_t d2)
157 {
158 struct ctlmsg *m = malloc(sizeof *m);
159
160 if (!m) {
161 log_err("malloc(%zu)", sizeof *m);
162 return -1;
163 }
164
165 memset(m, 0, sizeof *m);
166 m->type = htonl(type);
167 m->data = htonl(d);
168 m->data2 = htonl(d2);
169
170 return net_queue(p, MSG_SYNCCTL, (u_int8_t *)m, sizeof *m);
171 }
172
173 int
net_ctl_send_ack(struct syncpeer * p,enum CTLTYPE prevtype,u_int32_t code)174 net_ctl_send_ack(struct syncpeer *p, enum CTLTYPE prevtype, u_int32_t code)
175 {
176 return net_ctl_send(p, CTL_ACK, (u_int32_t)prevtype, code);
177 }
178
179 int
net_ctl_send_state(struct syncpeer * p)180 net_ctl_send_state(struct syncpeer *p)
181 {
182 return net_ctl_send(p, CTL_STATE, (u_int32_t)cfgstate.runstate, 0);
183 }
184
185 int
net_ctl_send_error(struct syncpeer * p,enum CTLTYPE prevtype)186 net_ctl_send_error(struct syncpeer *p, enum CTLTYPE prevtype)
187 {
188 return net_ctl_send(p, CTL_ERROR, (u_int32_t)prevtype, 0);
189 }
190
191 int
net_ctl_send_endsnap(struct syncpeer * p)192 net_ctl_send_endsnap(struct syncpeer *p)
193 {
194 return net_ctl_send(p, CTL_ENDSNAP, 0, 0);
195 }
196
197 /* After a CARP tracker state change, send an state ctl msg to all peers. */
198 void
net_ctl_update_state(void)199 net_ctl_update_state(void)
200 {
201 struct syncpeer *p;
202
203 /* We may have new peers available. */
204 net_connect();
205
206 for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
207 if (p->socket == -1)
208 continue;
209 log_msg(2, "net_ctl: sending my state %s to peer \"%s\"",
210 carp_state_name(cfgstate.runstate), p->name);
211 net_ctl_send_state(p);
212 }
213 }
214