/* * Copyright (c) 2017 SUSE LLC. * 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. */ #include #include sctp_main_t sctp_main; static u32 sctp_connection_bind (u32 session_index, transport_endpoint_t * tep) { sctp_main_t *tm = &sctp_main; sctp_connection_t *listener; void *iface_ip; u32 mtu = 1460; pool_get (tm->listener_pool, listener); clib_memset (listener, 0, sizeof (*listener)); listener->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx = SCTP_PRIMARY_PATH_IDX; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index = listener - tm->listener_pool; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.lcl_port = tep->port; /* If we are provided a sw_if_index, bind using one of its IPs */ if (ip_is_zero (&tep->ip, 1) && tep->sw_if_index != ENDPOINT_INVALID_INDEX) { if ((iface_ip = ip_interface_get_first_ip (tep->sw_if_index, tep->is_ip4))) ip_set (&tep->ip, iface_ip, tep->is_ip4); } ip_copy (&listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.lcl_ip, &tep->ip, tep->is_ip4); if (tep->sw_if_index != ENDPOINT_INVALID_INDEX) mtu = tep->is_ip4 ? vnet_sw_interface_get_mtu (vnet_get_main (), tep->sw_if_index, VNET_MTU_IP4) : vnet_sw_interface_get_mtu (vnet_get_main (), tep->sw_if_index, VNET_MTU_IP6); listener->sub_conn[SCTP_PRIMARY_PATH_IDX].PMTU = mtu; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.is_ip4 = tep->is_ip4; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.proto = TRANSPORT_PROTO_SCTP; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_s_index = session_index; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.fib_index = tep->fib_index; listener->state = SCTP_STATE_CLOSED; sctp_connection_timers_init (listener); return listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index; } u32 sctp_session_bind (u32 session_index, transport_endpoint_t * tep) { return sctp_connection_bind (session_index, tep); } static void sctp_connection_unbind (u32 listener_index) { sctp_main_t *tm = vnet_get_sctp_main (); sctp_connection_t *sctp_conn; sctp_conn = pool_elt_at_index (tm->listener_pool, listener_index); /* Poison the entry */ if (CLIB_DEBUG > 0) clib_memset (sctp_conn, 0xFA, sizeof (*sctp_conn)); pool_put_index (tm->listener_pool, listener_index); } u32 sctp_session_unbind (u32 listener_index) { sctp_connection_unbind (listener_index); return 0; } void sctp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add) { sctp_main_t *tm = &sctp_main; if (is_ip4) tm->punt_unknown4 = is_add; else tm->punt_unknown6 = is_add; } static int sctp_alloc_custom_local_endpoint (sctp_main_t * tm, ip46_address_t * lcl_addr, u16 * lcl_port, u8 is_ip4) { int index, port; if (is_ip4) { index = tm->last_v4_address_rotor++; if (tm->last_v4_address_rotor >= vec_len (tm->ip4_src_addresses)) tm->last_v4_address_rotor = 0; lcl_addr->ip4.as_u32 = tm->ip4_src_addresses[index].as_u32; } else { index = tm->last_v6_address_rotor++; if (tm->last_v6_address_rotor >= vec_len (tm->ip6_src_addresses)) tm->last_v6_address_rotor = 0; clib_memcpy (&lcl_addr->ip6, &tm->ip6_src_addresses[index], sizeof (ip6_address_t)); } port = transport_alloc_local_port (TRANSPORT_PROTO_SCTP, lcl_addr); if (port < 1) { clib_warning ("Failed to allocate src port"); return -1; } *lcl_port = port; return 0; } /** * Initialize all connection timers as invalid */ void sctp_connection_timers_init (sctp_connection_t * sctp_conn) { int i, j; /* Set all to invalid */ for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_conn->sub_conn[i].RTO = SCTP_RTO_INIT; for (j = 0; j < SCTP_N_TIMERS; j++) { sctp_conn->sub_conn[i].timers[j] = SCTP_TIMER_HANDLE_INVALID; } } } /** * Stop all connection timers */ void sctp_connection_timers_reset (sctp_connection_t * sctp_conn) { int i, j; for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { for (j = 0; j < SCTP_N_TIMERS; j++) sctp_timer_reset (sctp_conn, i, j); } } const char *sctp_fsm_states[] = { #define _(sym, str) str, foreach_sctp_fsm_state #undef _ }; u8 * format_sctp_state (u8 * s, va_list * args) { u32 state = va_arg (*args, u32); if (state < SCTP_N_STATES) s = format (s, "%s", sctp_fsm_states[state]); else s = format (s, "UNKNOWN (%d (0x%x))", state, state); return s; } u8 * format_sctp_connection_id (u8 * s, va_list * args) { sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *); if (!sctp_conn) return s; u8 i; for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { if (i > 0 && sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN) continue; if (sctp_conn->sub_conn[i].connection.is_ip4) { s = format (s, "[#%d][%s] %U:%d->%U:%d", sctp_conn->sub_conn[i].connection.thread_index, "S", format_ip4_address, &sctp_conn->sub_conn[i].connection.lcl_ip.ip4, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.lcl_port), format_ip4_address, &sctp_conn->sub_conn[i].connection.rmt_ip.ip4, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.rmt_port)); } else { s = format (s, "[#%d][%s] %U:%d->%U:%d", sctp_conn->sub_conn[i].connection.thread_index, "S", format_ip6_address, &sctp_conn->sub_conn[i].connection.lcl_ip.ip6, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.lcl_port), format_ip6_address, &sctp_conn->sub_conn[i].connection.rmt_ip.ip6, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.rmt_port)); } } return s; } u8 * format_sctp_connection (u8 * s, va_list * args) { sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *); u32 verbose = va_arg (*args, u32); if (!sctp_conn) return s; s = format (s, "%-50U", format_sctp_connection_id, sctp_conn); if (verbose) { s = format (s, "%-15U", format_sctp_state, sctp_conn->state); if (verbose > 1) s = format (s, "\n"); } return s; } /** * Initialize connection send variables. */ void sctp_init_snd_vars (sctp_connection_t * sctp_conn) { u32 time_now; /* * We use the time to randomize iss and for setting up the initial * timestamp. Make sure it's updated otherwise syn and ack in the * handshake may make it look as if time has flown in the opposite * direction for us. */ sctp_set_time_now (vlib_get_thread_index ()); time_now = sctp_time_now (); sctp_conn->local_initial_tsn = random_u32 (&time_now); sctp_conn->last_unacked_tsn = sctp_conn->local_initial_tsn; sctp_conn->next_tsn = sctp_conn->local_initial_tsn + 1; sctp_conn->remote_initial_tsn = 0x0; sctp_conn->last_rcvd_tsn = sctp_conn->remote_initial_tsn; } always_inline sctp_connection_t * sctp_sub_connection_add (u8 thread_index) { sctp_main_t *tm = vnet_get_sctp_main (); sctp_connection_t *sctp_conn = tm->connections[thread_index]; u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); ASSERT (subconn_idx < MAX_SCTP_CONNECTIONS); sctp_conn->sub_conn[subconn_idx].connection.c_index = sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.c_index; sctp_conn->sub_conn[subconn_idx].connection.thread_index = thread_index; sctp_conn->sub_conn[subconn_idx].subconn_idx = subconn_idx; return sctp_conn; } u8 sctp_sub_connection_add_ip4 (vlib_main_t * vm, ip4_address_t * lcl_addr, ip4_address_t * rmt_addr) { sctp_connection_t *sctp_conn = sctp_sub_connection_add (vm->thread_index); u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); if (subconn_idx == MAX_SCTP_CONNECTIONS) return SCTP_ERROR_MAX_CONNECTIONS; clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.lcl_ip, &lcl_addr, sizeof (lcl_addr)); clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.rmt_ip, &rmt_addr, sizeof (rmt_addr)); sctp_conn->forming_association_changed = 1; return SCTP_ERROR_NONE; } u8 sctp_sub_connection_del_ip4 (ip4_address_t * lcl_addr, ip4_address_t * rmt_addr) { sctp_main_t *sctp_main = vnet_get_sctp_main (); u32 thread_idx = vlib_get_thread_index (); u8 i; ASSERT (thread_idx == 0); for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_connection_t *sctp_conn = sctp_main->connections[thread_idx]; sctp_sub_connection_t *sub_conn = &sctp_main->connections[thread_idx]->sub_conn[i]; ip46_address_t *lcl_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.lcl_ip; ip46_address_t *rmt_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.rmt_ip; if (!sub_conn->connection.is_ip4) continue; if (lcl_ip->ip4.as_u32 == lcl_addr->as_u32 && rmt_ip->ip4.as_u32 == rmt_addr->as_u32) { sub_conn->state = SCTP_SUBCONN_STATE_DOWN; sctp_conn->forming_association_changed = 1; break; } } return SCTP_ERROR_NONE; } u8 sctp_sub_connection_add_ip6 (vlib_main_t * vm, ip6_address_t * lcl_addr, ip6_address_t * rmt_addr) { sctp_connection_t *sctp_conn = sctp_sub_connection_add (vm->thread_index); u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); if (subconn_idx == MAX_SCTP_CONNECTIONS) return SCTP_ERROR_MAX_CONNECTIONS; clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.lcl_ip, &lcl_addr, sizeof (lcl_addr)); clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.rmt_ip, &rmt_addr, sizeof (rmt_addr)); sctp_conn->forming_association_changed = 1; return SCTP_ERROR_NONE; } u8 sctp_sub_connection_del_ip6 (ip6_address_t * lcl_addr, ip6_address_t * rmt_addr) { sctp_main_t *sctp_main = vnet_get_sctp_main (); u32 thread_idx = vlib_get_thread_index (); u8 i; ASSERT (thread_idx == 0); for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_connection_t *sctp_conn = sctp_main->connections[thread_idx]; sctp_sub_connection_t *sub_conn = &sctp_main->connections[thread_idx]->sub_conn[i]; ip46_address_t *lcl_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.lcl_ip; ip46_address_t *rmt_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.rmt_ip; if (!sub_conn->connection.is_ip4) continue; if ((lcl_ip->ip6.as_u64[0] == lcl_addr->as_u64[0] && lcl_ip->ip6.as_u64[1] == lcl_addr->as_u64[1]) && (rmt_ip->ip6.as_u64[0] == rmt_addr->as_u64[0] && rmt_ip->ip6.as_u64[1] == rmt_addr->as_u64[1])) { sub_conn->state = SCTP_SUBCONN_STATE_DOWN; sctp_conn->forming_association_changed = 1; break; } } return SCTP_ERROR_NONE; } u8 sctp_configure (sctp_user_configuration_t config) { sctp_main_t *sctp_main = vnet_get_sctp_main (); u32 thread_idx = vlib_get_thread_index (); sctp_main->connections[thread_idx]->conn_config.never_delay_sack = config.never_delay_sack; sctp_main->connections[thread_idx]->conn_config.never_bundle = config.never_bundle; return 0; } sctp_connection_t * sctp_connection_new (u8 thread_index) { sctp_main_t *sctp_main = vnet_get_sctp_main (); sctp_connection_t *sctp_conn; pool_get (sctp_main->connections[thread_index], sctp_conn); clib_memset (sctp_conn, 0, sizeof (*sctp_conn)); sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx = SCTP_PRIMARY_PATH_IDX; sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index = sctp_conn - sctp_main->connections[thread_index]; sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_thread_index = thread_index; sctp_conn->local_tag = 0; return sctp_conn; } sctp_connection_t * sctp_half_open_connection_new (u8 thread_index) { sctp_main_t *tm = vnet_get_sctp_main (); sctp_connection_t *sctp_conn = 0; ASSERT (vlib_get_thread_index () == 0); pool_get (tm->half_open_connections, sctp_conn); clib_memset (sctp_conn, 0, sizeof (*sctp_conn)); sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index = sctp_conn - tm->half_open_connections; sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].subconn_idx = SCTP_PRIMARY_PATH_IDX; return sctp_conn; } static inline int sctp_connection_open (transport_endpoint_cfg_t * rmt) { sctp_main_t *tm = vnet_get_sctp_main (); sctp_connection_t *sctp_conn; ip46_address_t lcl_addr; u16 lcl_port; uword thread_id; u32 mtu = 1460; int rv; u8 idx = SCTP_PRIMARY_PATH_IDX; /* * Allocate local endpoint */ if ((rmt->is_ip4 && vec_len (tm->ip4_src_addresses)) || (!rmt->is_ip4 && vec_len (tm->ip6_src_addresses))) rv = sctp_alloc_custom_local_endpoint (tm, &lcl_addr, &lcl_port, rmt->is_ip4); else rv = transport_alloc_local_endpoint (TRANSPORT_PROTO_SCTP, rmt, &lcl_addr, &lcl_port); if (rv) return -1; /* * Create connection and send
.. _Routing:

.. toctree::

Connecting the two Containers
_____________________________

Now for connecting these two linux containers to VPP and pinging between them.

Enter container *cone*, and check the current network configuration:

.. code-block:: console
    
    root@cone:/# ip -o a
    1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
    1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
    30: veth0    inet 10.0.3.157/24 brd 10.0.3.255 scope global veth0\       valid_lft forever preferred_lft forever
    30: veth0    inet6 fe80::216:3eff:fee2:d0ba/64 scope link \       valid_lft forever preferred_lft forever
    32: veth_link1    inet6 fe80::2c9d:83ff:fe33:37e/64 scope link \       valid_lft forever preferred_lft forever

You can see that there are three network interfaces, *lo, veth0*, and *veth_link1*.

Notice that *veth_link1* has no assigned IP.

Check if the interfaces are down or up:

.. code-block:: console

    root@cone:/# ip link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    30: veth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 00:16:3e:e2:d0:ba brd ff:ff:ff:ff:ff:ff link-netnsid 0
    32: veth_link1@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 2e:9d:83:33:03:7e brd ff:ff:ff:ff:ff:ff link-netnsid 0

.. _networkNote:

.. note::

    Take note of the network index for **veth_link1**. In our case, it 32, and its parent index (the host machine, not the containers) is 33, shown by **veth_link1@if33**. Yours will most likely be different, but **please take note of these index's**.

Make sure your loopback interface is up, and assign an IP and gateway to veth_link1.

.. code-block:: console
    
    root@cone:/# ip link set dev lo up
    root@cone:/# ip addr add 172.16.1.2/24 dev veth_link1
    root@cone:/# ip link set dev veth_link1 up
    root@cone:/# dhclient -r
    root@cone:/# ip route add default via 172.16.1.1 dev veth_link1

Here, the IP is 172.16.1.2/24 and the gateway is 172.16.1.1.

Run some commands to verify the changes:

.. code-block:: console
    
    root@cone:/# ip -o a
    1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
    1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
    30: veth0    inet6 fe80::216:3eff:fee2:d0ba/64 scope link \       valid_lft forever preferred_lft forever
    32: veth_link1    inet 172.16.1.2/24 scope global veth_link1\       valid_lft forever preferred_lft forever
    32: veth_link1    inet6 fe80::2c9d:83ff:fe33:37e/64 scope link \       valid_lft forever preferred_lft forever

    root@cone:/# route
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    default         172.16.1.1      0.0.0.0         UG    0      0        0 veth_link1
    172.16.1.0      *               255.255.255.0   U     0      0        0 veth_link1


We see that the IP has been assigned, as well as our default gateway.

Now exit this container and repeat this process with container *ctwo*, except with IP 172.16.2.2/24 and gateway 172.16.2.1.


After that's done for *both* containers, exit from the container if you're in one:

.. code-block:: console
    
    root@ctwo:/# exit
    exit
    root@localhost:~#

In the machine running the containers, run **ip link** to see the host *veth* network interfaces, and their link with their respective *container veth's*.

.. code-block:: console
    
    root@localhost:~# ip link
    1: lo: <LOOPBACK> mtu 65536 qdisc noqueue state DOWN mode DEFAULT group default qlen 1
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether 08:00:27:33:82:8a brd ff:ff:ff:ff:ff:ff
    3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether 08:00:27:d9:9f:ac brd ff:ff:ff:ff:ff:ff
    4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
        link/ether 08:00:27:78:84:9d brd ff:ff:ff:ff:ff:ff
    5: lxcbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
        link/ether 00:16:3e:00:00:00 brd ff:ff:ff:ff:ff:ff
    19: veth0C2FL7@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master lxcbr0 state UP mode DEFAULT group default qlen 1000