aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/hicn/core/connection.c
blob: a9d632d52eb7ed32fa385114fa4be019a401a229 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
 * Copyright (c) 2021-2022 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file connection.c
 * @brief Implementation of hICN connections
 */

#include <assert.h>

#include <hicn/core/forwarder.h>
#include <hicn/core/listener.h>
#include <hicn/core/wldr.h>
#include <hicn/policy.h>
#include <hicn/util/log.h>

#include "connection.h"
#include "connection_vft.h"

// This is called by configuration
connection_t *connection_create(face_type_t type, const char *name,
                                const address_pair_t *pair,
                                const forwarder_t *forwarder) {
  assert(face_type_is_valid(type));
  assert(pair);
  assert(forwarder);

  /* initialized so that gcc-9 does not complain */
  face_type_t listener_type = FACE_TYPE_UNDEFINED;
  switch (type) {
    case FACE_TYPE_UDP:
      listener_type = FACE_TYPE_UDP_LISTENER;
      break;
    case FACE_TYPE_TCP:
      listener_type = FACE_TYPE_TCP_LISTENER;
      break;
    case FACE_TYPE_HICN:
      return NULL; /* Not implemented */
    case FACE_TYPE_HICN_LISTENER:
    case FACE_TYPE_UDP_LISTENER:
    case FACE_TYPE_TCP_LISTENER:
    case FACE_TYPE_UNDEFINED:
    case FACE_TYPE_N:
      return NULL;
  }

  listener_table_t *ltable = forwarder_get_listener_table(forwarder);
  listener_key_t key = listener_key_factory(pair->local, listener_type);

  listener_t *listener = listener_table_get_by_key(ltable, &key);
  if (!listener) {
    WITH_ERROR({
      char addr_str[NI_MAXHOST];
      int port;
      address_to_string(&pair->local, addr_str, &port);
      ERROR("Could not find listener to match address %s:%d", addr_str, port);
    })

    return NULL;
  }

  connection_table_t *table =
      forwarder_get_connection_table(listener->forwarder);
  unsigned connection_id = listener_create_connection(listener, name, pair);
  if (!connection_id_is_valid(connection_id)) return NULL;
  return connection_table_at(table, connection_id);
}

netdevice_type_t connection_get_netdevice_type(const char *interface_name) {
  if (strncmp(interface_name, "lo", 2) == 0) {
    return NETDEVICE_TYPE_LOOPBACK;
  }
  if ((strncmp(interface_name, "eth", 3) == 0) ||
      (strncmp(interface_name, "en", 2) == 0)) {
    /* eth* en* enx* */
    return NETDEVICE_TYPE_WIRED;
  }
  if (strncmp(interface_name, "wl", 2) == 0) {
    /* wlan* wlp* wlx* */
    return NETDEVICE_TYPE_WIFI;
  }
  if (strncmp(interface_name, "rmnet_ipa", 9) == 0) {
    /* Qualcomm IPA driver */
    return NETDEVICE_TYPE_UNDEFINED;
  }
  if ((strncmp(interface_name, "rmnet", 5) == 0) ||
      (strncmp(interface_name, "rev_rmnet", 9) == 0) ||
      (strncmp(interface_name, "ccmni", 5) == 0)) {
    /*
     * rmnet* (Qualcomm) ccmni* (MediaTek)
     */
    return NETDEVICE_TYPE_CELLULAR;
  }
  /* usb0 might be cellular (eg Zenfone2) */
  /* what about tethering */
  /* tun* dummy* ... */
  /* bnet* pan* hci* for bluetooth */
  return NETDEVICE_TYPE_UNDEFINED;
}

/**
 * @brief Initializes a connection
 *
 * @param [out] connection - Allocated connection buffer (eg. from pool) to be
 *      initialized.
 * @param [in] forwarder - forwarder_t to which the connection is associated.
 * This parameter needs to be non-NULL for connections receiving packets, such
 *      as TCP connections which are very close to UDP listeners, and unlike
 *      bound UDP connections).
 * @param [in] fd - A fd specific to the connection, or 0 if the connection
 *      should inherit the fd of the listener.
 * @return 0 if no error, -1 otherwise
 */
int connection_initialize(connection_t *connection, face_type_t type,
                          const char *name, const char *interface_name, int fd,
                          const address_pair_t *pair, bool local,
                          unsigned connection_id, listener_t *listener) {
  int rc;

  assert(connection);
  /* Interface name can be NULL eg always for TCP connnections */
  assert(pair);
  // assert(address_pair_is_valid(pair)); TODO: local addr in the pair is not
  // initialized for now

  if (fd == 0) WARN("Connection is not connected");

  *connection = (connection_t){
      .id = connection_id,
      .name = strdup(name),
      .type = type,
      .interface_name = strdup(interface_name),
      .pair = *pair,
      .fd = ((fd != 0) ? fd : listener_get_fd(listener)),
      .connected = (fd != 0),
      //        .up = true,
      .local = local,
      // XXX UDP should start UP, TCP DOWN until remove side answer ?
      .state = FACE_STATE_UNDEFINED,
      .admin_state = FACE_STATE_UP,
#ifdef WITH_POLICY
      .priority = 0,
#endif /* WITH_POLICY */

      .listener = listener,
      .closed = false,

      /* WLDR */
      .wldr = NULL,
      .wldr_autostart = true,
  };
  connection->stats.conn_id = connection_id;

  connection->interface_type =
      connection_get_netdevice_type(connection->interface_name);

#ifdef WITH_POLICY
  connection_clear_tags(connection);
  switch (connection->interface_type) {
#if 0
    case NETDEVICE_TYPE_LOOPBACK:
      connection_add_tag(connection, POLICY_TAG_LOOPBACK);
      break;
#endif
    case NETDEVICE_TYPE_WIRED:
      connection_add_tag(connection, POLICY_TAG_WIRED);
      break;
    case NETDEVICE_TYPE_WIFI:
      connection_add_tag(connection, POLICY_TAG_WIFI);
      break;
    case NETDEVICE_TYPE_CELLULAR:
      connection_add_tag(connection, POLICY_TAG_CELLULAR);
    default:
      break;
  }
#endif

  connection->data =
      malloc(connection_vft[get_protocol(connection->type)]->data_size);
  if (!connection->data) goto ERR_DATA;

  assert(connection_has_valid_id(connection));

  rc = connection_vft[get_protocol(connection->type)]->initialize(connection);
  if (rc < 0) {
    goto ERR_VFT;
  }

  if (connection->connected) {
    /*
     * The file descriptor is created by the listener. We assume for now that
     * all connections get their own fd, and we have to register it.
     *
     * TODO the connection has no more read callback, so we call the one from
     * the listener.
     */
    loop_fd_event_create(&connection->event_data, MAIN_LOOP, fd, listener,
                         (fd_callback_t)listener_read_callback, connection->id,
                         NULL);

    if (!connection->event_data) {
      goto ERR_REGISTER_FD;
    }

    if (loop_fd_event_register(connection->event_data) < 0) {
      goto ERR_REGISTER_FD;
    }
  }

  return 0;

ERR_REGISTER_FD:
#ifndef _WIN32
  close(fd);
#else
  closesocket(fd);
#endif
ERR_VFT:
  free(connection->data);
ERR_DATA:
  free(connection->interface_name);
  free(connection->name);
  return -1;
}

int connection_finalize(connection_t *connection) {
  assert(connection);
  assert(connection_has_valid_type(connection));

  if (connection->connected) {
    loop_event_unregister(connection->event_data);
    loop_event_free(connection->event_data);
  }

  if (connection->fd != 0) {  // Only if connected socket
#ifndef _WIN32
    close(connection->fd);
#else
    closesocket(connection->fd);
#endif
  }

  if (connection->wldr) wldr_free(connection->wldr);

  connection_vft[get_protocol(connection->type)]->finalize(connection);

  if (connection->data) free(connection->data);
  connection->data = NULL;
  if (connection->interface_name) free(connection->interface_name);
  connection->interface_name = NULL;
  if (connection->name) free(connection->name);
  connection->name = NULL;

  return 0;
}

bool connection_send_packet(const connection_t *connection,
                            const uint8_t *packet, size_t size) {
  assert(connection);
  assert(face_type_is_valid(connection->type));
  assert(packet);

  return connection_vft[get_protocol(connection->type)]->send_packet(
      connection, packet, size);
}

bool _connection_send(connection_t *connection, msgbuf_t *msgbuf, bool queue) {
  return connection_vft[get_protocol(connection->type)]->send(connection,
                                                              msgbuf, queue);
}

bool connection_flush(connection_t *connection) {
  return connection_vft[get_protocol(connection->type)]->flush(connection);
}

bool connection_send(connection_t *connection, off_t msgbuf_id, bool queue) {
  assert(connection);
  assert(msgbuf_id_is_valid(msgbuf_id));

  // if (!connection_is_up(connection))
  //     return false;

  const listener_t *listener = connection_get_listener(connection);
  const forwarder_t *forwarder = listener_get_forwarder(listener);
  const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder);
  msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id);

#if 0
  if (connection->wldr)
    wldr_set_label(connection->wldr, msgbuf);
  else
    msgbuf_reset_wldr_label(msgbuf);
#endif

  return _connection_send(connection, msgbuf, queue);
}

/*
 * here the wldr header is alreay set: this message is a retransmission or a
 * notification
 *
 * we need to recompute the path label since we always store a pointer to
 * the same message if this message will be sent again to someone else, the
 * new path label must be computed starting from the orignal label. Note
 * that we heve the same problem in case of PIT aggregation. That case is
 * handled inside the MessageProcessor. This is specific to WLDR
 * retransmittions. This is done only for data packets
 */
bool connection_resend(connection_t *connection, msgbuf_t *msgbuf,
                       bool notification) {
  assert(connection);
  assert(msgbuf);

  bool ret = false;

  if (!connection_is_up(connection)) return ret;

  ret = _connection_send(connection, msgbuf, false); /* no queueing */

  return ret;
}

/* WLDR */

void connection_wldr_allow_autostart(connection_t *connection, bool value) {
  connection->wldr_autostart = value;
}

bool connection_wldr_autostart_is_allowed(const connection_t *connection) {
  return connection->wldr_autostart;
}

void connection_wldr_enable(connection_t *connection, bool value) {
  if (connection_is_local(connection)) return;
  if (value) {
    if (connection->wldr) return;
    connection->wldr = wldr_create();
  } else {
    if (!connection->wldr) return;
    wldr_free(connection->wldr);
  }
}

bool connection_has_wldr(const connection_t *connection) {
  return !!connection->wldr;
}

void connection_wldr_detect_losses(const connection_t *connection,
                                   const msgbuf_t *msgbuf) {
  if (!connection->wldr) return;
  wldr_detect_losses(connection->wldr, connection, msgbuf);
}

void connection_wldr_handle_notification(const connection_t *connection,
                                         const msgbuf_t *msgbuf) {
  if (!connection->wldr) return;
  wldr_handle_notification(connection->wldr, connection, msgbuf);
}