aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/hicn/core/connection.c
blob: c8cc1d0b99da30c198c945d79de5b101653c00db (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
/*
 * Copyright (c) 2021 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/util/log.h>
#include <hicn/core/wldr.h>

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

#define _conn_var(x) _connection_##x

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

  face_type_t listener_type;
  switch (type) {
    case FACE_TYPE_UDP:
      listener_type = FACE_TYPE_UDP_LISTENER;
      break;
    case FACE_TYPE_TCP:
      listener_type = FACE_TYPE_TCP_LISTENER;
      break;
    default:
      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);
}

/**
 * @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->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, 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;
}

int 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(const connection_t *connection, msgbuf_t *msgbuf,
                      bool queue) {
  return connection_vft[get_protocol(connection->type)]->send(connection,
                                                              msgbuf, queue);
}

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

bool connection_send(const 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 (connection->wldr)
    wldr_set_label(connection->wldr, msgbuf);
  else
    msgbuf_reset_wldr_label(msgbuf);

  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(const 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);
}