summaryrefslogtreecommitdiffstats
path: root/src/vnet/sr/sr_api.c
blob: f4e1c3460f73db5315d34c58482aba5051cac7b5 (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
/*
 *------------------------------------------------------------------
 * sr_api.c - ipv6 segment routing api
 *
 * Copyright (c) 2016 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/vnet.h>
#include <vnet/sr/sr.h>
#include <vlibmemory/api.h>

#include <vnet/interface.h>
#include <vnet/api_errno.h>
#include <vnet/feature/feature.h>

#include <vnet/vnet_msg_enum.h>

#define vl_typedefs		/* define message structures */
#include <vnet/vnet_all_api_h.h>
#undef vl_typedefs

#define vl_endianfun		/* define message structures */
#include <vnet/vnet_all_api_h.h>
#undef vl_endianfun

/* instantiate all the print functions we know about */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_printfun
#include <vnet/vnet_all_api_h.h>
#undef vl_printfun

#include <vlibapi/api_helper_macros.h>

#define foreach_vpe_api_msg                             \
_(SR_LOCALSID_ADD_DEL, sr_localsid_add_del)             \
_(SR_POLICY_DEL, sr_policy_del)                         \
_(SR_STEERING_ADD_DEL, sr_steering_add_del)
//_(SR_LOCALSIDS, sr_localsids_dump)
//_(SR_LOCALSID_BEHAVIORS, sr_localsid_behaviors_dump)

static void vl_api_sr_localsid_add_del_t_handler
  (vl_api_sr_localsid_add_del_t * mp)
{
  vl_api_sr_localsid_add_del_reply_t *rmp;
  int rv = 0;
/*
 * int sr_cli_localsid (char is_del, ip6_address_t *localsid_addr,
 *  char end_psp, u8 behavior, u32 sw_if_index, u32 vlan_index, u32 fib_table,
 *  ip46_address_t *nh_addr, void *ls_plugin_mem)
 */
  rv = sr_cli_localsid (mp->is_del,
			(ip6_address_t *) & mp->localsid_addr,
			mp->end_psp,
			mp->behavior,
			ntohl (mp->sw_if_index),
			ntohl (mp->vlan_index),
			ntohl (mp->fib_table),
			(ip46_address_t *) & mp->nh_addr, NULL);

  REPLY_MACRO (VL_API_SR_LOCALSID_ADD_DEL_REPLY);
}

static void
vl_api_sr_policy_add_t_handler (vl_api_sr_policy_add_t * mp)
{
  vl_api_sr_policy_add_reply_t *rmp;
  ip6_address_t *segments = 0, *seg;
  ip6_address_t *this_address = (ip6_address_t *) mp->segments;

  int i;
  for (i = 0; i < mp->n_segments; i++)
    {
      vec_add2 (segments, seg, 1);
      clib_memcpy (seg->as_u8, this_address->as_u8, sizeof (*this_address));
      this_address++;
    }

/*
 * sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
 *                u32 weight, u8 behavior, u32 fib_table, u8 is_encap)
 */
  int rv = 0;
  rv = sr_policy_add ((ip6_address_t *) & mp->bsid_addr,
		      segments,
		      ntohl (mp->weight),
		      mp->type, ntohl (mp->fib_table), mp->is_encap);

  REPLY_MACRO (VL_API_SR_POLICY_ADD_REPLY);
}

static void
vl_api_sr_policy_mod_t_handler (vl_api_sr_policy_mod_t * mp)
{
  vl_api_sr_policy_mod_reply_t *rmp;

  ip6_address_t *segments = 0, *seg;
  ip6_address_t *this_address = (ip6_address_t *) mp->segments;

  int i;
  for (i = 0; i < mp->n_segments; i++)
    {
      vec_add2 (segments, seg, 1);
      clib_memcpy (seg->as_u8, this_address->as_u8, sizeof (*this_address));
      this_address++;
    }

  int rv = 0;
/*
 * int
 * sr_policy_mod(ip6_address_t *bsid, u32 index, u32 fib_table,
 *               u8 operation, ip6_address_t *segments, u32 sl_index,
 *               u32 weight, u8 is_encap)
 */
  rv = sr_policy_mod ((ip6_address_t *) & mp->bsid_addr,
		      ntohl (mp->sr_policy_index),
		      ntohl (mp->fib_table),
		      mp->operation,
		      segments, ntohl (mp->sl_index), ntohl (mp->weight));

  REPLY_MACRO (VL_API_SR_POLICY_MOD_REPLY);
}

static void
vl_api_sr_policy_del_t_handler (vl_api_sr_policy_del_t * mp)
{
  vl_api_sr_policy_del_reply_t *rmp;
  int rv = 0;
/*
 * int
 * sr_policy_del (ip6_address_t *bsid, u32 index)
 */
  rv = sr_policy_del ((ip6_address_t *) & mp->bsid_addr,
		      ntohl (mp->sr_policy_index));

  REPLY_MACRO (VL_API_SR_POLICY_DEL_REPLY);
}

static void vl_api_sr_steering_add_del_t_handler
  (vl_api_sr_steering_add_del_t * mp)
{
  vl_api_sr_steering_add_del_reply_t *rmp;
  int rv = 0;
/*
 * int
 * sr_steering_policy(int is_del, ip6_address_t *bsid, u32 sr_policy_index,
 *  u32 table_id, ip46_address_t *prefix, u32 mask_width, u32 sw_if_index,
 *  u8 traffic_type)
 */
  rv = sr_steering_policy (mp->is_del,
			   (ip6_address_t *) & mp->bsid_addr,
			   ntohl (mp->sr_policy_index),
			   ntohl (mp->table_id),
			   (ip46_address_t *) & mp->prefix_addr,
			   ntohl (mp->mask_width),
			   ntohl (mp->sw_if_index), mp->traffic_type);

  REPLY_MACRO (VL_API_SR_STEERING_ADD_DEL_REPLY);
}

/*
 * sr_api_hookup
 * Add vpe's API message handlers to the table.
 * vlib has alread mapped shared memory and
 * added the client registration handlers.
 * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process()
 */
#define vl_msg_name_crc_list
#include <vnet/vnet_all_api_h.h>
#undef vl_msg_name_crc_list

static void
setup_message_id_table (api_main_t * am)
{
#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
  foreach_vl_msg_name_crc_sr;
#undef _
}

static clib_error_t *
sr_api_hookup (vlib_main_t * vm)
{
  api_main_t *am = &api_main;

#define _(N,n)                                                  \
    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
                           vl_api_##n##_t_handler,              \
                           vl_noop_handler,                     \
                           vl_api_##n##_t_endian,               \
                           vl_api_##n##_t_print,                \
                           sizeof(vl_api_##n##_t), 1);
  foreach_vpe_api_msg;
#undef _

  /*
   * Manually register the sr policy add msg, so we trace
   * enough bytes to capture a typical segment list
   */
  vl_msg_api_set_handlers (VL_API_SR_POLICY_ADD,
			   "sr_policy_add",
			   vl_api_sr_policy_add_t_handler,
			   vl_noop_handler,
			   vl_api_sr_policy_add_t_endian,
			   vl_api_sr_policy_add_t_print, 256, 1);

  /*
   * Manually register the sr policy mod msg, so we trace
   * enough bytes to capture a typical segment list
   */
  vl_msg_api_set_handlers (VL_API_SR_POLICY_MOD,
			   "sr_policy_mod",
			   vl_api_sr_policy_mod_t_handler,
			   vl_noop_handler,
			   vl_api_sr_policy_mod_t_endian,
			   vl_api_sr_policy_mod_t_print, 256, 1);

  /*
   * Set up the (msg_name, crc, message-id) table
   */
  setup_message_id_table (am);

  return 0;
}

VLIB_API_INIT_FUNCTION (sr_api_hookup);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
n>, 0); if (fatal) { syslog (LOG_ERR | LOG_DAEMON, "%s", msg); os_exit (1); } else clib_warning ("%s", msg); vec_free (msg); } static clib_error_t * setup_signal_handlers (unix_main_t * um) { uword i; struct sigaction sa; for (i = 1; i < 32; i++) { memset (&sa, 0, sizeof (sa)); sa.sa_sigaction = (void *) unix_signal_handler; sa.sa_flags = SA_SIGINFO; switch (i) { /* these signals take the default action */ case SIGABRT: case SIGKILL: case SIGSTOP: case SIGUSR1: case SIGUSR2: continue; /* ignore SIGPIPE, SIGCHLD */ case SIGPIPE: case SIGCHLD: sa.sa_sigaction = (void *) SIG_IGN; break; /* catch and handle all other signals */ default: break; } if (sigaction (i, &sa, 0) < 0) return clib_error_return_unix (0, "sigaction %U", format_signal, i); } return 0; } static void unix_error_handler (void *arg, u8 * msg, int msg_len) { unix_main_t *um = arg; /* Echo to stderr when interactive. */ if (um->flags & UNIX_FLAG_INTERACTIVE) { CLIB_UNUSED (int r) = write (2, msg, msg_len); } else { char save = msg[msg_len - 1]; /* Null Terminate. */ msg[msg_len - 1] = 0; syslog (LOG_ERR | LOG_DAEMON, "%s", msg); msg[msg_len - 1] = save; } } void vlib_unix_error_report (vlib_main_t * vm, clib_error_t * error) { unix_main_t *um = &unix_main; if (um->flags & UNIX_FLAG_INTERACTIVE || error == 0) return; { char save; u8 *msg; u32 msg_len; msg = error->what; msg_len = vec_len (msg); /* Null Terminate. */ save = msg[msg_len - 1]; msg[msg_len - 1] = 0; syslog (LOG_ERR | LOG_DAEMON, "%s", msg); msg[msg_len - 1] = save; } } static uword startup_config_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { unix_main_t *um = &unix_main; u8 *buf = 0; uword l, n = 1; vlib_process_suspend (vm, 2.0); while (um->unix_config_complete == 0) vlib_process_suspend (vm, 0.1); if (um->startup_config_filename) { unformat_input_t sub_input; int fd; struct stat s; char *fn = (char *) um->startup_config_filename; fd = open (fn, O_RDONLY); if (fd < 0) { clib_warning ("failed to open `%s'", fn); return 0; } if (fstat (fd, &s) < 0) { clib_warning ("failed to stat `%s'", fn); bail: close (fd); return 0; } if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode))) { clib_warning ("not a regular file: `%s'", fn); goto bail; } while (n > 0) { l = vec_len (buf); vec_resize (buf, 4096); n = read (fd, buf + l, 4096); if (n > 0) { _vec_len (buf) = l + n; if (n < 4096) break; } else break; } if (um->log_fd && vec_len (buf)) { u8 *lv = 0; lv = format (lv, "%U: ***** Startup Config *****\n%v", format_timeval, 0 /* current bat-time */ , 0 /* current bat-format */ , buf); { int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv)); } vec_reset_length (lv); lv = format (lv, "%U: ***** End Startup Config *****\n", format_timeval, 0 /* current bat-time */ , 0 /* current bat-format */ ); { int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv)); } vec_free (lv); } if (vec_len (buf)) { unformat_init_vector (&sub_input, buf); vlib_cli_input (vm, &sub_input, 0, 0); /* frees buf for us */ unformat_free (&sub_input); } close (fd); } return 0; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (startup_config_node,static) = { .function = startup_config_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "startup-config-process", }; /* *INDENT-ON* */ static clib_error_t * unix_config (vlib_main_t * vm, unformat_input_t * input) { unix_main_t *um = &unix_main; clib_error_t *error = 0; gid_t gid; int pidfd = -1; /* Defaults */ um->cli_pager_buffer_limit = UNIX_CLI_DEFAULT_PAGER_LIMIT; um->cli_history_limit = UNIX_CLI_DEFAULT_HISTORY; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { char *cli_prompt; if (unformat (input, "interactive")) um->flags |= UNIX_FLAG_INTERACTIVE; else if (unformat (input, "nodaemon")) um->flags |= UNIX_FLAG_NODAEMON; else if (unformat (input, "cli-prompt %s", &cli_prompt)) vlib_unix_cli_set_prompt (cli_prompt); else if (unformat (input, "cli-listen %s", &um->cli_listen_socket.config)) ; else if (unformat (input, "runtime-dir %s", &um->runtime_dir)) ; else if (unformat (input, "cli-line-mode")) um->cli_line_mode = 1; else if (unformat (input, "cli-no-banner")) um->cli_no_banner = 1; else if (unformat (input, "cli-no-pager")) um->cli_no_pager = 1; else if (unformat (input, "cli-pager-buffer-limit %d", &um->cli_pager_buffer_limit)) ; else if (unformat (input, "cli-history-limit %d", &um->cli_history_limit)) ; else if (unformat (input, "coredump-size")) { uword coredump_size = 0; if (unformat (input, "unlimited")) { coredump_size = RLIM_INFINITY; } else if (!unformat (input, "%U", unformat_memory_size, &coredump_size)) { return clib_error_return (0, "invalid coredump-size parameter `%U'", format_unformat_error, input); } const struct rlimit new_limit = { coredump_size, coredump_size }; if (0 != setrlimit (RLIMIT_CORE, &new_limit)) { clib_unix_warning ("prlimit() failed"); } } else if (unformat (input, "full-coredump")) { int fd; fd = open ("/proc/self/coredump_filter", O_WRONLY); if (fd >= 0) { if (write (fd, "0x6f\n", 5) != 5) clib_unix_warning ("coredump filter write failed!"); close (fd); } else clib_unix_warning ("couldn't open /proc/self/coredump_filter"); } else if (unformat (input, "startup-config %s", &um->startup_config_filename)) ; else if (unformat (input, "exec %s", &um->startup_config_filename)) ; else if (unformat (input, "log %s", &um->log_filename)) { um->log_fd = open ((char *) um->log_filename, O_CREAT | O_WRONLY | O_APPEND, 0644); if (um->log_fd < 0) { clib_warning ("couldn't open log '%s'\n", um->log_filename); um->log_fd = 0; } else { u8 *lv = 0; lv = format (0, "%U: ***** Start: PID %d *****\n", format_timeval, 0 /* current bat-time */ , 0 /* current bat-format */ , getpid ()); { int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv)); } vec_free (lv); } } else if (unformat (input, "gid %U", unformat_unix_gid, &gid)) { if (setegid (gid) == -1) return clib_error_return_unix (0, "setegid"); } else if (unformat (input, "pidfile %s", &um->pidfile)) ; else return clib_error_return (0, "unknown input `%U'", format_unformat_error, input); } if (um->runtime_dir == 0) { uid_t uid = geteuid (); if (uid == 00) um->runtime_dir = format (0, "/run/%s%c", vlib_default_runtime_dir, 0); else um->runtime_dir = format (0, "/run/user/%u/%s%c", uid, vlib_default_runtime_dir, 0); } error = setup_signal_handlers (um); if (error) return error; if (um->pidfile) { if ((error = vlib_unix_validate_runtime_file (um, (char *) um->pidfile, &um->pidfile))) return error; if (((pidfd = open ((char *) um->pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)) { return clib_error_return_unix (0, "open"); } } if (!(um->flags & UNIX_FLAG_INTERACTIVE)) { openlog (vm->name, LOG_CONS | LOG_PERROR | LOG_PID, LOG_DAEMON); clib_error_register_handler (unix_error_handler, um); if (!(um->flags & UNIX_FLAG_NODAEMON) && daemon ( /* chdir to / */ 0, /* stdin/stdout/stderr -> /dev/null */ 0) < 0) clib_error_return (0, "daemon () fails"); } if (pidfd >= 0) { u8 *lv = format (0, "%d", getpid ()); if (write (pidfd, (char *) lv, vec_len (lv)) != vec_len (lv)) { vec_free (lv); close (pidfd); return clib_error_return_unix (0, "write"); } vec_free (lv); close (pidfd); } um->unix_config_complete = 1; return 0; } /* unix { ... } configuration. */ /*? * * @cfgcmd{interactive} * Attach CLI to stdin/out and provide a debugging command line interface. * Implies @c nodaemon. * * @cfgcmd{nodaemon} * Do not fork or background the VPP process. Typically used when invoking * VPP applications from a process monitor. * * @cfgcmd{exec, &lt;filename&gt;} * @par <code>startup-config &lt;filename&gt;</code> * Read startup operational configuration from @c filename. * The contents of the file will be performed as though entered at the CLI. * The two keywords are aliases for the same function; if both are specified, * only the last will have an effect. * * @cfgcmd{log, &lt;filename&gt;} * Logs the startup configuration and all subsequent CLI commands in * @c filename. * Very useful in situations where folks don't remember or can't be bothered * to include CLI commands in bug reports. * * @cfgcmd{pidfile, &lt;filename&gt;} * Writes the pid of the main thread in @c filename. * * @cfgcmd{full-coredump} * Ask the Linux kernel to dump all memory-mapped address regions, instead * of just text+data+bss. * * @cfgcmd{runtime-dir} * Define directory where VPP is going to store all runtime files. * Default is /run/vpp. * * @cfgcmd{cli-listen, &lt;address:port&gt;} * Bind the CLI to listen at the address and port given. @clocalhost * on TCP port @c 5002, given as <tt>cli-listen localhost:5002</tt>, * is typical. * * @cfgcmd{cli-line-mode} * Disable character-by-character I/O on stdin. Useful when combined with, * for example, <tt>emacs M-x gud-gdb</tt>. * * @cfgcmd{cli-prompt, &lt;string&gt;} * Configure the CLI prompt to be @c string. * * @cfgcmd{cli-history-limit, &lt;nn&gt;} * Limit commmand history to @c nn lines. A value of @c 0 * disables command history. Default value: @c 50 * * @cfgcmd{cli-no-banner} * Disable the login banner on stdin and Telnet connections. * * @cfgcmd{cli-no-pager} * Disable the output pager. * * @cfgcmd{cli-pager-buffer-limit, &lt;nn&gt;} * Limit pager buffer to @c nn lines of output. * A value of @c 0 disables the pager. Default value: @c 100000 ?*/ VLIB_EARLY_CONFIG_FUNCTION (unix_config, "unix"); static clib_error_t * unix_exit (vlib_main_t * vm) { /* Close syslog connection. */ closelog (); return 0; } VLIB_MAIN_LOOP_EXIT_FUNCTION (unix_exit); u8 **vlib_thread_stacks; static uword thread0 (uword arg) { vlib_main_t *vm = (vlib_main_t *) arg; unformat_input_t input; int i; unformat_init_command_line (&input, (char **) vm->argv); i = vlib_main (vm, &input); unformat_free (&input); return i; } u8 * vlib_thread_stack_init (uword thread_index) { vec_validate (vlib_thread_stacks, thread_index); vlib_thread_stacks[thread_index] = clib_mem_alloc_aligned (VLIB_THREAD_STACK_SIZE, VLIB_THREAD_STACK_SIZE); /* * Disallow writes to the bottom page of the stack, to * catch stack overflows. */ if (mprotect (vlib_thread_stacks[thread_index], clib_mem_get_page_size (), PROT_READ) < 0) clib_unix_warning ("thread stack"); return vlib_thread_stacks[thread_index]; } int vlib_unix_main (int argc, char *argv[]) { vlib_main_t *vm = &vlib_global_main; /* one and only time for this! */ unformat_input_t input; clib_error_t *e; int i; vm->argv = (u8 **) argv; vm->name = argv[0]; vm->heap_base = clib_mem_get_heap (); ASSERT (vm->heap_base); unformat_init_command_line (&input, (char **) vm->argv); if ((e = vlib_plugin_config (vm, &input))) { clib_error_report (e); return 1; } unformat_free (&input); i = vlib_plugin_early_init (vm); if (i) return i; unformat_init_command_line (&input, (char **) vm->argv); if (vm->init_functions_called == 0) vm->init_functions_called = hash_create (0, /* value bytes */ 0); e = vlib_call_all_config_functions (vm, &input, 1 /* early */ ); if (e != 0) { clib_error_report (e); return 1; } unformat_free (&input); vlib_thread_stack_init (0); __os_thread_index = 0; vm->thread_index = 0; i = clib_calljmp (thread0, (uword) vm, (void *) (vlib_thread_stacks[0] + VLIB_THREAD_STACK_SIZE)); return i; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */