summaryrefslogtreecommitdiffstats
path: root/src/vnet/sctp/sctp_format.c
blob: 49ee04def659de8be8341093f6e6a33942677ddd (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
/*
 * 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 <vnet/sctp/sctp.h>

/* Format SCTP header. */
u8 *
format_sctp_header (u8 * s, va_list * args)
{
  return NULL;
}

u8 *
format_sctp_tx_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);

  return NULL;
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
d='n429' href='#n429'>429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/*
 * Copyright (c) 2018 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.
 */

#include <vnet/ip6-nd/ip6_ra.h>

#include <vnet/ip/ip6.h>
#include <vnet/ip/ip6_link.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/fib/fib_table.h>
#include <signal.h>
#include <math.h>

typedef struct
{
  u32 sw_if_index;
  u8 address_length;
  ip6_address_t address;
  f64 due_time;
} slaac_address_t;

typedef struct
{
  u32 sw_if_index;
  ip6_address_t router_address;
  f64 due_time;
} default_route_t;

typedef struct
{
  u8 enabled;
  u8 install_default_routes;
} interface_config_t;

typedef struct
{
  u8 enabled;
  u8 events_on;

  interface_config_t *config_by_sw_if_index;
  slaac_address_t *slaac_address_pool;
  default_route_t *default_route_pool;

  /* binary API client */
  u8 api_connected;
  u32 my_client_index;

  /* logging */
  vlib_log_class_t log_class;

  /* convenience */
  vlib_main_t *vlib_main;
  vnet_main_t *vnet_main;
  u32 node_index;
} rd_cp_main_t;

rd_cp_main_t rd_cp_main;

enum
{
  RD_CP_EVENT_INTERRUPT,
};

#define vl_api_ip6_nd_address_autoconfig_t_print vl_noop_handler

static void
router_solicitation_start_stop (u32 sw_if_index, u8 start)
{
  rd_cp_main_t *rm = &rd_cp_main;
  icmp6_send_router_solicitation_params_t params = { 0, };

  if (start)
    {
      params.irt = 1;
      params.mrt = 120;
    }

  icmp6_send_router_solicitation (rm->vlib_main, sw_if_index, !start,
				  &params);
}

static void interrupt_process (void);

static int
add_slaac_address (vlib_main_t * vm, u32 sw_if_index, u8 address_length,
		   const ip6_address_t * address, f64 due_time)
{
  rd_cp_main_t *rm = &rd_cp_main;
  slaac_address_t *slaac_address;
  clib_error_t *rv = 0;

  pool_get_zero (rm->slaac_address_pool, slaac_address);

  slaac_address->sw_if_index = sw_if_index;
  slaac_address->address_length = address_length;
  slaac_address->address = *address;
  slaac_address->due_time = due_time;

  rv =
    ip6_add_del_interface_address (vm, sw_if_index, &slaac_address->address,
				   address_length, 0);

  return rv != 0;
}

static void
add_default_route (vlib_main_t * vm, u32 sw_if_index,
		   const ip6_address_t * next_hop_address, f64 due_time)
{
  rd_cp_main_t *rm = &rd_cp_main;
  default_route_t *default_route;

  pool_get_zero (rm->default_route_pool, default_route);

  default_route->sw_if_index = sw_if_index;
  default_route->router_address = *next_hop_address;
  default_route->due_time = due_time;

  {
    u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
							 default_route->
							 sw_if_index);
    fib_prefix_t pfx = {
      .fp_proto = FIB_PROTOCOL_IP6,
    };
    ip46_address_t nh = {
      .ip6 = default_route->router_address,
    };
    fib_table_entry_update_one_path (fib_index, &pfx,
				     FIB_SOURCE_API,
				     FIB_ENTRY_FLAG_NONE,
				     DPO_PROTO_IP6,
				     &nh,
				     default_route->sw_if_index,
				     0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
  }
}

static int
remove_slaac_address (vlib_main_t * vm, slaac_address_t * slaac_address)
{
  rd_cp_main_t *rm = &rd_cp_main;
  clib_error_t *rv = 0;

  rv = ip6_add_del_interface_address (vm, slaac_address->sw_if_index,
				      &slaac_address->address,
				      slaac_address->address_length, 1);

  pool_put (rm->slaac_address_pool, slaac_address);

  return rv != 0;
}

static void
remove_default_route (vlib_main_t * vm, default_route_t * default_route)
{
  rd_cp_main_t *rm = &rd_cp_main;

  {
    u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
							 default_route->
							 sw_if_index);
    fib_prefix_t pfx = {
      .fp_proto = FIB_PROTOCOL_IP6,
    };
    ip46_address_t nh = {
      .ip6 = default_route->router_address,
    };
    fib_table_entry_path_remove (fib_index, &pfx,
				 FIB_SOURCE_API,
				 DPO_PROTO_IP6,
				 &nh,
				 default_route->sw_if_index,
				 0, 1, FIB_ROUTE_PATH_FLAG_NONE);
  }

  pool_put (rm->default_route_pool, default_route);
}

static u32
get_interface_mac_address (u32 sw_if_index, u8 mac[])
{
  rd_cp_main_t *rm = &rd_cp_main;
  vnet_sw_interface_t *si;
  ethernet_interface_t *eth_if = 0;

  if (!vnet_sw_interface_is_api_valid (rm->vnet_main, sw_if_index))
    {
      vlib_log_warn (rm->log_class, "Invalid sw_if_index");
      return 1;
    }

  si = vnet_get_sup_sw_interface (rm->vnet_main, sw_if_index);
  if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
    eth_if = ethernet_get_interface (&ethernet_main, si->hw_if_index);

  if (!eth_if)
    {
      vlib_log_warn (rm->log_class, "Failed to get hardware interface");
      return 1;
    }

  clib_memcpy_fast (mac, eth_if->address, 6);

  return 0;
}

static u8
ip6_prefixes_equal (ip6_address_t * prefix1, ip6_address_t * prefix2, u8 len)
{
  if (len >= 64)
    {
      if (prefix1->as_u64[0] != prefix2->as_u64[0])
	return 0;
      if (len == 64)
	return 1;
      return prefix1->as_u64[1] >> (128 - len) ==
	prefix2->as_u64[1] >> (128 - len);
    }
  return prefix1->as_u64[0] >> (64 - len) == prefix2->as_u64[0] >> (64 - len);
}

#define PREFIX_FLAG_A (1 << 6)
#define PREFIX_FLAG_L (1 << 7)

static void
ip6_ra_report_handler (const ip6_ra_report_t * r)
{
  rd_cp_main_t *rm = &rd_cp_main;
  vlib_main_t *vm = rm->vlib_main;
  interface_config_t *if_config;
  default_route_t *default_route;
  slaac_address_t *slaac_address;
  u32 sw_if_index;
  u16 router_lifetime_in_sec;
  u32 n_prefixes;
  ra_report_prefix_info_t *prefix;
  u8 mac[6];
  f64 current_time;
  u32 i;

  current_time = vlib_time_now (vm);

  sw_if_index = r->sw_if_index;

  if (sw_if_index >= vec_len (rm->config_by_sw_if_index))
    return;
  if_config = &rm->config_by_sw_if_index[sw_if_index];

  if (if_config->install_default_routes)
    {
      router_lifetime_in_sec = r->router_lifetime_in_sec;
      u8 route_already_present = 0;
      /* *INDENT-OFF* */
      pool_foreach (default_route, rm->default_route_pool,
      ({
        if (default_route->sw_if_index != sw_if_index)
          ;
        else if (0 != memcmp (&default_route->router_address,
                              &r->router_address, 16))
          ;
        else
          {
            route_already_present = 1;
            goto default_route_pool_foreach_out;
          }
      }));
      /* *INDENT-ON* */
    default_route_pool_foreach_out:

      if (!route_already_present)
	{
	  if (router_lifetime_in_sec != 0)
	    add_default_route (vm, sw_if_index, &r->router_address,
			       current_time + router_lifetime_in_sec);
	}
      else
	{
	  if (router_lifetime_in_sec != 0)
	    default_route->due_time = current_time + router_lifetime_in_sec;
	  else
	    remove_default_route (vm, default_route);
	}
    }

  if (get_interface_mac_address (sw_if_index, mac) != 0)
    {
      vlib_log_warn (rm->log_class, "Error getting MAC address");
      return;
    }

  if (!if_config->enabled)
    return;

  n_prefixes = vec_len (r->prefixes);
  for (i = 0; i < n_prefixes; i++)
    {
      ip6_address_t *dst_address;
      u8 prefix_length;
      u32 valid_time;
      u32 preferred_time;
      f64 due_time;

      prefix = &r->prefixes[i];

      if (!(prefix->flags & PREFIX_FLAG_A))
	continue;

      dst_address = &prefix->prefix.fp_addr.ip6;
      prefix_length = prefix->prefix.fp_len;

      if (ip6_address_is_link_local_unicast (dst_address))
	continue;

      valid_time = prefix->valid_time;
      preferred_time = prefix->preferred_time;

      if (preferred_time > valid_time)
	continue;

      if (prefix_length != 64)
	continue;

      u8 address_already_present = 0;
      /* *INDENT-OFF* */
      pool_foreach (slaac_address, rm->slaac_address_pool,
      ({
        if (slaac_address->sw_if_index != sw_if_index)
          ;
        else if (slaac_address->address_length != prefix_length)
          ;
        else if (!ip6_prefixes_equal (&slaac_address->address, dst_address,
                                 prefix_length))
          ;
        else
          {
            address_already_present = 1;
            goto slaac_address_pool_foreach_out;
          }
      }));
      /* *INDENT-ON* */
    slaac_address_pool_foreach_out:

      if (address_already_present)
	{
	  f64 remaining_life_time = slaac_address->due_time - current_time;
	  if (valid_time > 2 * 60 * 60 || valid_time > remaining_life_time)
	    slaac_address->due_time = current_time + valid_time;
	  else if (remaining_life_time > 2 * 60 * 60)
	    slaac_address->due_time = current_time + 2 * 60 * 60;
	  continue;
	}

      if (valid_time == 0)
	continue;

      due_time = current_time + valid_time;

      ip6_address_t addr;
      addr.as_u64[0] = dst_address->as_u64[0];
      /* Invert the "u" bit */
      addr.as_u8[8] = mac[0] ^ (1 << 1);
      addr.as_u8[9] = mac[1];
      addr.as_u8[10] = mac[2];
      addr.as_u8[11] = 0xFF;
      addr.as_u8[12] = 0xFE;
      addr.as_u8[13] = mac[3];
      addr.as_u8[14] = mac[4];
      addr.as_u8[15] = mac[5];

      add_slaac_address (vm, sw_if_index, prefix_length, &addr, due_time);
    }

  interrupt_process ();

  return;
}

static uword
rd_cp_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
{
  uword *event_data = 0;
  rd_cp_main_t *rm = &rd_cp_main;
  slaac_address_t *slaac_address;
  default_route_t *default_route;
  f64 sleep_time = 1e9;
  f64 current_time;
  f64 due_time;

  while (1)
    {
      vlib_process_wait_for_event_or_clock (vm, sleep_time);
      vlib_process_get_events (vm, &event_data);

      vec_reset_length (event_data);

      current_time = vlib_time_now (vm);
      do
	{
	  due_time = current_time + 1e9;
	  u32 index;
	  /*
	   * we do not use pool_foreach() to iterate over pool elements here
	   * as we are removing elements inside the loop body
	   */
          /* *INDENT-OFF* */
          pool_foreach_index (index, rm->slaac_address_pool,
          ({
            slaac_address = pool_elt_at_index(rm->slaac_address_pool, index);
            if (slaac_address->due_time > current_time)
              {
                if (slaac_address->due_time < due_time)
                  due_time = slaac_address->due_time;
              }
            else
              {
                u32 sw_if_index = slaac_address->sw_if_index;
                remove_slaac_address (vm, slaac_address);
                /* make sure ip6 stays enabled */
                ip6_link_enable (sw_if_index, NULL);
              }
          }));
          pool_foreach_index (index, rm->default_route_pool,
          ({
            default_route = pool_elt_at_index(rm->default_route_pool, index);
            if (default_route->due_time > current_time)
              {
                if (default_route->due_time < due_time)
                  due_time = default_route->due_time;
              }
            else
              remove_default_route (vm, default_route);
          }));
          /* *INDENT-ON* */
	  current_time = vlib_time_now (vm);
	}
      while (due_time < current_time);

      sleep_time = due_time - current_time;
    }

  return 0;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (rd_cp_process_node) = {
    .function = rd_cp_process,
    .type = VLIB_NODE_TYPE_PROCESS,
    .name = "rd-cp-process",
};
/* *INDENT-ON* */

static void
interrupt_process (void)
{
  rd_cp_main_t *rm = &rd_cp_main;
  vlib_main_t *vm = rm->vlib_main;

  vlib_process_signal_event (vm, rd_cp_process_node.index,
			     RD_CP_EVENT_INTERRUPT, 0);
}

int
rd_cp_set_address_autoconfig (u32 sw_if_index,
			      u8 enable, u8 install_default_routes)
{
  rd_cp_main_t *rm = &rd_cp_main;
  vlib_main_t *vm = rm->vlib_main;
  vnet_main_t *vnm = rm->vnet_main;
  interface_config_t *if_config;
  interface_config_t empty_config = { 0, 0 };
  slaac_address_t *slaac_address;
  default_route_t *default_route;

  if (!enable)
    install_default_routes = 0;

  if (!vnet_sw_interface_is_api_valid (vnm, sw_if_index))
    {
      vlib_log_warn (rm->log_class, "Invalid sw_if_index");
      return 1;
    }

  if (!rm->enabled)
    {
      /* process kickoff */
      interrupt_process ();
      rm->enabled = 1;
    }

  vec_validate_init_empty (rm->config_by_sw_if_index, sw_if_index,
			   empty_config);
  if_config = &rm->config_by_sw_if_index[sw_if_index];

  if (!if_config->enabled && enable)
    ip6_link_enable (sw_if_index, NULL);

  if ((!if_config->enabled && enable)
      || (!if_config->install_default_routes && install_default_routes))
    router_solicitation_start_stop (sw_if_index, 1);
  else if (if_config->enabled && !enable)
    router_solicitation_start_stop (sw_if_index, 0);

  if (if_config->enabled && !enable)
    {
      /* *INDENT-OFF* */
      pool_foreach (slaac_address, rm->slaac_address_pool,
      ({
          remove_slaac_address (vm, slaac_address);
      }));
      /* *INDENT-ON* */
    }
  if (if_config->install_default_routes && !install_default_routes)
    {
      /* *INDENT-OFF* */
      pool_foreach (default_route, rm->default_route_pool,
      ({
          remove_default_route (vm, default_route);
      }));
      /* *INDENT-ON* */
    }

  if_config->enabled = enable;
  if_config->install_default_routes = install_default_routes;

  return 0;
}

static clib_error_t *
ip6_nd_address_autoconfig (vlib_main_t * vm,
			   unformat_input_t * input, vlib_cli_command_t * cmd)
{
  rd_cp_main_t *rm = &rd_cp_main;
  vnet_main_t *vnm = rm->vnet_main;
  clib_error_t *error = 0;
  u32 sw_if_index = ~0;
  u8 enable = 1;
  u8 default_route = 0;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat
	  (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
	;
      if (unformat (input, "default-route"))
	default_route = 1;
      if (unformat (input, "disable"))
	enable = 0;
      else
	break;
    }

  if (sw_if_index != ~0)
    {
      if (rd_cp_set_address_autoconfig (sw_if_index, enable, default_route) !=
	  0)
	error = clib_error_return (0, "Invalid sw_if_index");
    }
  else
    error = clib_error_return (0, "Missing sw_if_index");

  return error;
}

/*?
 * This command is used to enable ND address autoconfiguration
 * on particular interface including setting up default routes.
 *
 * @cliexpar
 * @parblock
 * Example of how to enable ND address autoconfiguration:
 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0}
 * Example of how to enable ND address autoconfiguration
 * with setting up default routes:
 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 default-route}
 * Example of how to disable ND address autoconfiguration:
 * @cliexcmd{ip6 nd address autoconfig GigabitEthernet2/0/0 disable}
 * @endparblock
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip6_nd_address_autoconfig_command, static) = {
  .path = "ip6 nd address autoconfig",
  .short_help = "ip6 nd address autoconfig <interface> [default-route|disable]",
  .function = ip6_nd_address_autoconfig,
};
/* *INDENT-ON* */

static clib_error_t *
rd_cp_init (vlib_main_t * vm)
{
  rd_cp_main_t *rm = &rd_cp_main;

  rm->vlib_main = vm;
  rm->vnet_main = vnet_get_main ();
  rm->node_index = rd_cp_process_node.index;

  rm->log_class = vlib_log_register_class ("rd_cp", 0);

  ip6_ra_report_register (ip6_ra_report_handler);

  return (NULL);
}

VLIB_INIT_FUNCTION (rd_cp_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */