1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3  * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <unistd.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 
24 #include "lldpctl.h"
25 #include "atom.h"
26 #include "../compat/compat.h"
27 #include "../ctl.h"
28 #include "../log.h"
29 
30 const char*
lldpctl_get_default_transport(void)31 lldpctl_get_default_transport(void)
32 {
33 	return LLDPD_CTL_SOCKET;
34 }
35 
36 /* Connect to the remote end */
37 static int
sync_connect(lldpctl_conn_t * lldpctl)38 sync_connect(lldpctl_conn_t *lldpctl)
39 {
40 	return ctl_connect(lldpctl->ctlname);
41 }
42 
43 /* Synchronously send data to remote end. */
44 static ssize_t
sync_send(lldpctl_conn_t * lldpctl,const uint8_t * data,size_t length,void * user_data)45 sync_send(lldpctl_conn_t *lldpctl,
46     const uint8_t *data, size_t length, void *user_data)
47 {
48 	struct lldpctl_conn_sync_t *conn = user_data;
49 	ssize_t nb;
50 
51 	if (conn->fd == -1 &&
52 	    ((conn->fd = sync_connect(lldpctl)) == -1)) {
53 		return LLDPCTL_ERR_CANNOT_CONNECT;
54 	}
55 
56 	while ((nb = write(conn->fd, data, length)) == -1) {
57 		if (errno == EAGAIN || errno == EINTR) continue;
58 		return LLDPCTL_ERR_CALLBACK_FAILURE;
59 	}
60 	return nb;
61 }
62 
63 /* Statically receive data from remote end. */
64 static ssize_t
sync_recv(lldpctl_conn_t * lldpctl,const uint8_t * data,size_t length,void * user_data)65 sync_recv(lldpctl_conn_t *lldpctl,
66     const uint8_t *data, size_t length, void *user_data)
67 {
68 	struct lldpctl_conn_sync_t *conn = user_data;
69 	ssize_t nb;
70 	size_t remain, offset = 0;
71 
72 	if (conn->fd == -1 &&
73 	    ((conn->fd = sync_connect(lldpctl)) == -1)) {
74 		lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
75 		return LLDPCTL_ERR_CANNOT_CONNECT;
76 	}
77 
78 	remain = length;
79 	do {
80 		if ((nb = read(conn->fd, (unsigned char*)data + offset, remain)) == -1) {
81 			if (errno == EAGAIN || errno == EINTR)
82 				continue;
83 			return LLDPCTL_ERR_CALLBACK_FAILURE;
84 		}
85 		remain -= nb;
86 		offset += nb;
87 	} while (remain > 0 && nb != 0);
88 	return offset;
89 }
90 
91 lldpctl_conn_t*
lldpctl_new(lldpctl_send_callback send,lldpctl_recv_callback recv,void * user_data)92 lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
93 {
94 	return lldpctl_new_name(lldpctl_get_default_transport(), send, recv, user_data);
95 }
96 
97 lldpctl_conn_t*
lldpctl_new_name(const char * ctlname,lldpctl_send_callback send,lldpctl_recv_callback recv,void * user_data)98 lldpctl_new_name(const char *ctlname, lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
99 {
100 	lldpctl_conn_t *conn = NULL;
101 	struct lldpctl_conn_sync_t *data = NULL;
102 
103 	/* Both callbacks are mandatory or should be NULL. */
104 	if (send && !recv) return NULL;
105 	if (recv && !send) return NULL;
106 
107 	if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL)
108 		return NULL;
109 
110 	conn->ctlname = strdup(ctlname);
111 	if (conn->ctlname == NULL) {
112 		free(conn);
113 		return NULL;
114 	}
115 	if (!send && !recv) {
116 		if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
117 			free(conn->ctlname);
118 			free(conn);
119 			return NULL;
120 		}
121 		data->fd = -1;
122 		conn->send = sync_send;
123 		conn->recv = sync_recv;
124 		conn->user_data = data;
125 	} else {
126 		conn->send = send;
127 		conn->recv = recv;
128 		conn->user_data = user_data;
129 	}
130 
131 	return conn;
132 }
133 
134 int
lldpctl_release(lldpctl_conn_t * conn)135 lldpctl_release(lldpctl_conn_t *conn)
136 {
137 	if (conn == NULL) return 0;
138 	free(conn->ctlname);
139 	if (conn->send == sync_send) {
140 		struct lldpctl_conn_sync_t *data = conn->user_data;
141 		if (data->fd != -1) close(data->fd);
142 		free(conn->user_data);
143 	}
144 	free(conn->input_buffer);
145 	free(conn->output_buffer);
146 	free(conn);
147 	return 0;
148 }
149 
150 /**
151  * Request some bytes if they are not already here.
152  *
153  * @param conn   The connection to lldpd.
154  * @param length The number of requested bytes.
155  * @return A negative integer if we can't have the bytes or the number of bytes we got.
156  */
157 ssize_t
_lldpctl_needs(lldpctl_conn_t * conn,size_t length)158 _lldpctl_needs(lldpctl_conn_t *conn, size_t length)
159 {
160 	uint8_t *buffer = NULL;
161 	ssize_t  rc;
162 
163 	if ((buffer = malloc(length)) == NULL)
164 		return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
165 	rc = conn->recv(conn, buffer, length, conn->user_data);
166 	if (rc < 0) {
167 		free(buffer);
168 		return SET_ERROR(conn, rc);
169 	}
170 	if (rc == 0) {
171 		free(buffer);
172 		return SET_ERROR(conn, LLDPCTL_ERR_EOF);
173 	}
174 	rc = lldpctl_recv(conn, buffer, rc);
175 	free(buffer);
176 	if (rc < 0)
177 		return SET_ERROR(conn, rc);
178 	RESET_ERROR(conn);
179 	return rc;
180 }
181 
182 static int
check_for_notification(lldpctl_conn_t * conn)183 check_for_notification(lldpctl_conn_t *conn)
184 {
185 	struct lldpd_neighbor_change *change;
186 	void *p;
187 	int rc;
188 	lldpctl_change_t type;
189 	lldpctl_atom_t *interface = NULL, *neighbor = NULL;
190 	rc = ctl_msg_recv_unserialized(&conn->input_buffer,
191 	    &conn->input_buffer_len,
192 	    NOTIFICATION,
193 	    &p,
194 	    &MARSHAL_INFO(lldpd_neighbor_change));
195 	if (rc != 0) return rc;
196 	change = p;
197 
198 	/* We have a notification, call the callback */
199 	if (conn->watch_cb || conn->watch_cb2) {
200 		switch (change->state) {
201 		case NEIGHBOR_CHANGE_DELETED: type = lldpctl_c_deleted; break;
202 		case NEIGHBOR_CHANGE_ADDED: type = lldpctl_c_added; break;
203 		case NEIGHBOR_CHANGE_UPDATED: type = lldpctl_c_updated; break;
204 		default:
205 			log_warnx("control", "unknown notification type (%d)",
206 			    change->state);
207 			goto end;
208 		}
209 		interface = _lldpctl_new_atom(conn, atom_interface,
210 		    change->ifname);
211 		if (interface == NULL) goto end;
212 		neighbor = _lldpctl_new_atom(conn, atom_port, 0,
213 		    NULL, change->neighbor, NULL);
214 		if (neighbor == NULL) goto end;
215 		if (conn->watch_cb)
216 			conn->watch_cb(conn, type, interface, neighbor, conn->watch_data);
217 		else
218 			conn->watch_cb2(type, interface, neighbor, conn->watch_data);
219 		conn->watch_triggered = 1;
220 		goto end;
221 	}
222 
223 end:
224 	if (interface) lldpctl_atom_dec_ref(interface);
225 	if (neighbor) lldpctl_atom_dec_ref(neighbor);
226 	else {
227 		lldpd_chassis_cleanup(change->neighbor->p_chassis, 1);
228 		lldpd_port_cleanup(change->neighbor, 1);
229 		free(change->neighbor);
230 	}
231 	free(change->ifname);
232 	free(change);
233 
234 	/* Indicate if more data remains in the buffer for processing */
235 	return (rc);
236 }
237 
238 ssize_t
lldpctl_recv(lldpctl_conn_t * conn,const uint8_t * data,size_t length)239 lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
240 {
241 
242 	RESET_ERROR(conn);
243 
244 	if (length == 0) return 0;
245 
246 	/* Received data should be appended to the input buffer. */
247 	if (conn->input_buffer == NULL) {
248 		conn->input_buffer_len = 0;
249 		if ((conn->input_buffer = malloc(length)) == NULL)
250 			return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
251 	} else {
252 		uint8_t *new = realloc(conn->input_buffer, conn->input_buffer_len + length);
253 		if (new == NULL)
254 			return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
255 		conn->input_buffer = new;
256 	}
257 	memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
258 	conn->input_buffer_len += length;
259 
260 	/* Read all notifications */
261 	while(!check_for_notification(conn));
262 
263 	RESET_ERROR(conn);
264 
265 	return conn->input_buffer_len;
266 }
267 
268 int
lldpctl_process_conn_buffer(lldpctl_conn_t * conn)269 lldpctl_process_conn_buffer(lldpctl_conn_t *conn)
270 {
271 	int rc;
272 
273 	rc = check_for_notification(conn);
274 
275 	RESET_ERROR(conn);
276 
277 	return rc;
278 }
279 
280 ssize_t
lldpctl_send(lldpctl_conn_t * conn)281 lldpctl_send(lldpctl_conn_t *conn)
282 {
283 	/* Send waiting data. */
284 	ssize_t rc;
285 
286 	RESET_ERROR(conn);
287 
288 	if (!conn->output_buffer) return 0;
289 	rc = conn->send(conn,
290 	    conn->output_buffer, conn->output_buffer_len,
291 	    conn->user_data);
292 	if (rc < 0) return SET_ERROR(conn, rc);
293 
294 	/* "Shrink" the output buffer. */
295 	if (rc == conn->output_buffer_len) {
296 		free(conn->output_buffer);
297 		conn->output_buffer = NULL;
298 		conn->output_buffer_len = 0;
299 		RESET_ERROR(conn);
300 		return rc;
301 	}
302 	conn->output_buffer_len -= rc;
303 	memmove(conn->output_buffer, conn->output_buffer + rc, conn->output_buffer_len);
304 	/* We don't shrink the buffer. It will be either freed or shrinked later */
305 	RESET_ERROR(conn);
306 	return rc;
307 }
308