]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lib/connection.c
priv: disable LLDP in firmware for Intel X7xx cards on FreeBSD
[thirdparty/lldpd.git] / src / lib / connection.c
CommitLineData
4b292b55
VB
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>
4e5f34c5 20#include <string.h>
4b292b55
VB
21#include <sys/socket.h>
22#include <sys/un.h>
23
24#include "lldpctl.h"
a5f98717 25#include "atom.h"
4b292b55
VB
26#include "../compat/compat.h"
27#include "../ctl.h"
4e90a9e0 28#include "../log.h"
4b292b55 29
8b549648 30const char *
4b292b55
VB
31lldpctl_get_default_transport(void)
32{
33 return LLDPD_CTL_SOCKET;
34}
35
36/* Connect to the remote end */
37static int
0262adbb 38sync_connect(lldpctl_conn_t *lldpctl)
4b292b55 39{
0262adbb 40 return ctl_connect(lldpctl->ctlname);
4b292b55
VB
41}
42
43/* Synchronously send data to remote end. */
44static ssize_t
8b549648 45sync_send(lldpctl_conn_t *lldpctl, const uint8_t *data, size_t length, void *user_data)
4b292b55
VB
46{
47 struct lldpctl_conn_sync_t *conn = user_data;
a6509ccc 48 ssize_t nb;
4b292b55 49
8b549648 50 if (conn->fd == -1 && ((conn->fd = sync_connect(lldpctl)) == -1)) {
4b292b55
VB
51 return LLDPCTL_ERR_CANNOT_CONNECT;
52 }
53
54 while ((nb = write(conn->fd, data, length)) == -1) {
55 if (errno == EAGAIN || errno == EINTR) continue;
56 return LLDPCTL_ERR_CALLBACK_FAILURE;
57 }
58 return nb;
59}
60
bdc954b7 61/* Statically receive data from remote end. */
4b292b55 62static ssize_t
8b549648 63sync_recv(lldpctl_conn_t *lldpctl, const uint8_t *data, size_t length, void *user_data)
4b292b55
VB
64{
65 struct lldpctl_conn_sync_t *conn = user_data;
62f2faac 66 ssize_t nb;
e581ec30 67 size_t remain, offset = 0;
4b292b55 68
8b549648 69 if (conn->fd == -1 && ((conn->fd = sync_connect(lldpctl)) == -1)) {
4b292b55
VB
70 lldpctl->error = LLDPCTL_ERR_CANNOT_CONNECT;
71 return LLDPCTL_ERR_CANNOT_CONNECT;
72 }
73
e581ec30
VB
74 remain = length;
75 do {
8b549648
VB
76 if ((nb = read(conn->fd, (unsigned char *)data + offset, remain)) ==
77 -1) {
78 if (errno == EAGAIN || errno == EINTR) continue;
62f2faac
VB
79 return LLDPCTL_ERR_CALLBACK_FAILURE;
80 }
e581ec30
VB
81 remain -= nb;
82 offset += nb;
8568f6cd 83 } while (remain > 0 && nb != 0);
e581ec30 84 return offset;
4b292b55
VB
85}
86
8b549648 87lldpctl_conn_t *
4b292b55 88lldpctl_new(lldpctl_send_callback send, lldpctl_recv_callback recv, void *user_data)
0262adbb
ZM
89{
90 return lldpctl_new_name(lldpctl_get_default_transport(), send, recv, user_data);
91}
92
8b549648
VB
93lldpctl_conn_t *
94lldpctl_new_name(const char *ctlname, lldpctl_send_callback send,
95 lldpctl_recv_callback recv, void *user_data)
4b292b55
VB
96{
97 lldpctl_conn_t *conn = NULL;
98 struct lldpctl_conn_sync_t *data = NULL;
99
100 /* Both callbacks are mandatory or should be NULL. */
101 if (send && !recv) return NULL;
102 if (recv && !send) return NULL;
103
8b549648 104 if ((conn = calloc(1, sizeof(lldpctl_conn_t))) == NULL) return NULL;
4b292b55 105
0262adbb
ZM
106 conn->ctlname = strdup(ctlname);
107 if (conn->ctlname == NULL) {
108 free(conn);
109 return NULL;
110 }
4b292b55
VB
111 if (!send && !recv) {
112 if ((data = malloc(sizeof(struct lldpctl_conn_sync_t))) == NULL) {
a3222449 113 free(conn->ctlname);
4b292b55
VB
114 free(conn);
115 return NULL;
116 }
117 data->fd = -1;
118 conn->send = sync_send;
119 conn->recv = sync_recv;
120 conn->user_data = data;
121 } else {
122 conn->send = send;
123 conn->recv = recv;
124 conn->user_data = user_data;
125 }
126
127 return conn;
128}
129
130int
131lldpctl_release(lldpctl_conn_t *conn)
132{
133 if (conn == NULL) return 0;
0262adbb 134 free(conn->ctlname);
4b292b55
VB
135 if (conn->send == sync_send) {
136 struct lldpctl_conn_sync_t *data = conn->user_data;
137 if (data->fd != -1) close(data->fd);
138 free(conn->user_data);
139 }
140 free(conn->input_buffer);
141 free(conn->output_buffer);
142 free(conn);
143 return 0;
144}
145
146/**
147 * Request some bytes if they are not already here.
148 *
149 * @param conn The connection to lldpd.
150 * @param length The number of requested bytes.
151 * @return A negative integer if we can't have the bytes or the number of bytes we got.
152 */
153ssize_t
154_lldpctl_needs(lldpctl_conn_t *conn, size_t length)
155{
575c586c 156 uint8_t *buffer;
8b549648 157 ssize_t rc;
4b292b55 158
e19e50f6 159 if ((buffer = calloc(1, length)) == NULL)
4b292b55
VB
160 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
161 rc = conn->recv(conn, buffer, length, conn->user_data);
162 if (rc < 0) {
163 free(buffer);
164 return SET_ERROR(conn, rc);
165 }
166 if (rc == 0) {
167 free(buffer);
168 return SET_ERROR(conn, LLDPCTL_ERR_EOF);
169 }
170 rc = lldpctl_recv(conn, buffer, rc);
171 free(buffer);
8b549648 172 if (rc < 0) return SET_ERROR(conn, rc);
4b292b55
VB
173 RESET_ERROR(conn);
174 return rc;
175}
176
1fa64c11 177static int
4e90a9e0
VB
178check_for_notification(lldpctl_conn_t *conn)
179{
180 struct lldpd_neighbor_change *change;
0e940d8d 181 void *p;
4e90a9e0
VB
182 int rc;
183 lldpctl_change_t type;
184 lldpctl_atom_t *interface = NULL, *neighbor = NULL;
8b549648
VB
185 rc = ctl_msg_recv_unserialized(&conn->input_buffer, &conn->input_buffer_len,
186 NOTIFICATION, &p, &MARSHAL_INFO(lldpd_neighbor_change));
1fa64c11 187 if (rc != 0) return rc;
0e940d8d 188 change = p;
4e90a9e0
VB
189
190 /* We have a notification, call the callback */
9d7e82a1 191 if (conn->watch_cb || conn->watch_cb2) {
4e90a9e0 192 switch (change->state) {
8b549648
VB
193 case NEIGHBOR_CHANGE_DELETED:
194 type = lldpctl_c_deleted;
195 break;
196 case NEIGHBOR_CHANGE_ADDED:
197 type = lldpctl_c_added;
198 break;
199 case NEIGHBOR_CHANGE_UPDATED:
200 type = lldpctl_c_updated;
201 break;
4e90a9e0 202 default:
6f8925be 203 log_warnx("control", "unknown notification type (%d)",
4e90a9e0
VB
204 change->state);
205 goto end;
206 }
8b549648 207 interface = _lldpctl_new_atom(conn, atom_interface, change->ifname);
4e90a9e0 208 if (interface == NULL) goto end;
8b549648
VB
209 neighbor =
210 _lldpctl_new_atom(conn, atom_port, 0, NULL, change->neighbor, NULL);
4e90a9e0 211 if (neighbor == NULL) goto end;
9d7e82a1 212 if (conn->watch_cb)
8b549648
VB
213 conn->watch_cb(conn, type, interface, neighbor,
214 conn->watch_data);
9d7e82a1
VB
215 else
216 conn->watch_cb2(type, interface, neighbor, conn->watch_data);
4e90a9e0
VB
217 conn->watch_triggered = 1;
218 goto end;
219 }
220
221end:
222 if (interface) lldpctl_atom_dec_ref(interface);
8b549648
VB
223 if (neighbor)
224 lldpctl_atom_dec_ref(neighbor);
4e90a9e0
VB
225 else {
226 lldpd_chassis_cleanup(change->neighbor->p_chassis, 1);
227 lldpd_port_cleanup(change->neighbor, 1);
228 free(change->neighbor);
229 }
230 free(change->ifname);
231 free(change);
1fa64c11
ST
232
233 /* Indicate if more data remains in the buffer for processing */
234 return (rc);
4e90a9e0
VB
235}
236
4b292b55
VB
237ssize_t
238lldpctl_recv(lldpctl_conn_t *conn, const uint8_t *data, size_t length)
239{
240
241 RESET_ERROR(conn);
242
243 if (length == 0) return 0;
244
245 /* Received data should be appended to the input buffer. */
246 if (conn->input_buffer == NULL) {
247 conn->input_buffer_len = 0;
248 if ((conn->input_buffer = malloc(length)) == NULL)
249 return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
250 } else {
8b549648
VB
251 uint8_t *new =
252 realloc(conn->input_buffer, conn->input_buffer_len + length);
253 if (new == NULL) return SET_ERROR(conn, LLDPCTL_ERR_NOMEM);
4b292b55
VB
254 conn->input_buffer = new;
255 }
256 memcpy(conn->input_buffer + conn->input_buffer_len, data, length);
257 conn->input_buffer_len += length;
4e90a9e0 258
db3731d0 259 /* Read all notifications */
8b549648
VB
260 while (!check_for_notification(conn))
261 ;
4e90a9e0 262
4b292b55 263 RESET_ERROR(conn);
4e90a9e0
VB
264
265 return conn->input_buffer_len;
4b292b55
VB
266}
267
cb38337c
VB
268int
269lldpctl_process_conn_buffer(lldpctl_conn_t *conn)
1fa64c11
ST
270{
271 int rc;
272
273 rc = check_for_notification(conn);
274
275 RESET_ERROR(conn);
276
277 return rc;
278}
279
4b292b55
VB
280ssize_t
281lldpctl_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;
8b549648 289 rc = conn->send(conn, conn->output_buffer, conn->output_buffer_len,
4b292b55
VB
290 conn->user_data);
291 if (rc < 0) return SET_ERROR(conn, rc);
292
293 /* "Shrink" the output buffer. */
294 if (rc == conn->output_buffer_len) {
295 free(conn->output_buffer);
296 conn->output_buffer = NULL;
297 conn->output_buffer_len = 0;
298 RESET_ERROR(conn);
299 return rc;
300 }
301 conn->output_buffer_len -= rc;
302 memmove(conn->output_buffer, conn->output_buffer + rc, conn->output_buffer_len);
303 /* We don't shrink the buffer. It will be either freed or shrinked later */
304 RESET_ERROR(conn);
305 return rc;
306}