summaryrefslogtreecommitdiffstats
path: root/src/plugins/gbp/gbp_contract.c
blob: 552201a684b077f2fdc7ad725700d283fe88b04a (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

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * srv6_end_m_gtp6_d_di_di.c
 *
 * Copyright (c) 2019 Arrcus Inc 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/adj/adj.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <srv6-mobile/mobile.h>

srv6_end_main_v6_decap_di_t srv6_end_main_v6_decap_di;

static void
clb_dpo_lock_srv6_end_m_gtp6_d_di (dpo_id_t * dpo)
{
}

static void
clb_dpo_unlock_srv6_end_m_gtp6_d_di (dpo_id_t * dpo)
{
}

static u8 *
clb_dpo_format_srv6_end_m_gtp6_d_di (u8 * s, va_list * args)
{
  index_t index = va_arg (*args, index_t);
  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);

  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
}

const static dpo_vft_t dpo_vft = {
  .dv_lock = clb_dpo_lock_srv6_end_m_gtp6_d_di,
  .dv_unlock = clb_dpo_unlock_srv6_end_m_gtp6_d_di,
  .dv_format = clb_dpo_format_srv6_end_m_gtp6_d_di,
};

const static char *const srv6_end_m_gtp6_d_di_nodes[] = {
  "srv6-end-m-gtp6-d-di",
  NULL,
};

const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
  [DPO_PROTO_IP6] = srv6_end_m_gtp6_d_di_nodes,
};

static u8 fn_name[] = "SRv6-End.M.GTP6.D.DI-plugin";
static u8 keyword_str[] = "end.m.gtp6.d.di";
static u8 def_str[] =
  "Endpoint function with drop-in dencapsulation for IPv6/GTP tunnel";
static u8 param_str[] = "<sr-prefix>/<sr-prefixlen> [nhtype <nhtype>]";

static u8 *
clb_format_srv6_end_m_gtp6_d_di (u8 * s, va_list * args)
{
  srv6_end_gtp6_param_t *ls_mem = va_arg (*args, void *);

  s = format (s, "SRv6 End gtp6.d Drop-in\n\t");

  s =
    format (s, "SR Prefix: %U/%d", format_ip6_address, &ls_mem->sr_prefix,
	    ls_mem->sr_prefixlen);

  if (ls_mem->nhtype != SRV6_NHTYPE_NONE)
    {
      if (ls_mem->nhtype == SRV6_NHTYPE_IPV4)
	s = format (s, ", NHType IPv4\n");
      else if (ls_mem->nhtype == SRV6_NHTYPE_IPV6)
	s = format (s, ", NHType IPv6\n");
      else if (ls_mem->nhtype == SRV6_NHTYPE_NON_IP)
	s = format (s, ", NHType Non-IP\n");
      else
	s = format (s, ", NHType Unknow(%d)\n", ls_mem->nhtype);
    }
  else
    s = format (s, "\n");

  return 
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * gbp.h : Group Based Policy
 *
 * 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 <plugins/gbp/gbp.h>
#include <plugins/gbp/gbp_bridge_domain.h>
#include <plugins/gbp/gbp_route_domain.h>
#include <plugins/gbp/gbp_policy_dpo.h>

#include <vnet/dpo/load_balance.h>
#include <vnet/dpo/drop_dpo.h>

/**
 * Single contract DB instance
 */
gbp_contract_db_t gbp_contract_db;

gbp_contract_t *gbp_contract_pool;

vlib_log_class_t gc_logger;

fib_node_type_t gbp_next_hop_fib_type;

gbp_rule_t *gbp_rule_pool;
gbp_next_hop_t *gbp_next_hop_pool;

#define GBP_CONTRACT_DBG(...)                           \
    vlib_log_notice (gc_logger, __VA_ARGS__);

/* Adjacency packet/byte counters indexed by adjacency index. */
vlib_combined_counter_main_t gbp_contract_permit_counters = {
  .name = "gbp-contracts-permit",
  .stat_segment_name = "/net/gbp/contract/permit",
};

vlib_combined_counter_main_t gbp_contract_drop_counters = {
  .name = "gbp-contracts-drop",
  .stat_segment_name = "/net/gbp/contract/drop",
};

index_t
gbp_rule_alloc (gbp_rule_action_t action,
		gbp_hash_mode_t hash_mode, index_t * nhs)
{
  gbp_rule_t *gu;

  pool_get_zero (gbp_rule_pool, gu);

  gu->gu_hash_mode = hash_mode;
  gu->gu_nhs = nhs;
  gu->gu_action = action;

  return (gu - gbp_rule_pool);
}

index_t
gbp_next_hop_alloc (const ip46_address_t * ip,
		    index_t grd, const mac_address_t * mac, index_t gbd)
{
  fib_protocol_t fproto;
  gbp_next_hop_t *gnh;

  pool_get_zero (gbp_next_hop_pool, gnh);

  fib_node_init (&gnh->gnh_node, gbp_next_hop_fib_type);

  ip46_address_copy (&gnh->gnh_ip, ip);
  mac_address_copy (&gnh->gnh_mac, mac);

  gnh->gnh_rd = grd;
  gnh->gnh_bd = gbd;

  FOR_EACH_FIB_IP_PROTOCOL (fproto) gnh->gnh_ai[fproto] = INDEX_INVALID;

  return (gnh - gbp_next_hop_pool);
}

static inline gbp_next_hop_t *
gbp_next_hop_get (index_t gui)
{
  return (pool_elt_at_index (gbp_next_hop_pool, gui));
}

static void
gbp_contract_rules_free (index_t * rules)
{
  index_t *gui, *gnhi;

  vec_foreach (gui, rules)
  {
    gbp_policy_node_t pnode;
    fib_protocol_t fproto;
    gbp_next_hop_t *gnh;
    gbp_rule_t *gu;

    gu = gbp_rule_get (*gui);

    FOR_EACH_GBP_POLICY_NODE (pnode)
    {
      FOR_EACH_FIB_IP_PROTOCOL (fproto)
      {
	dpo_reset (&gu->gu_dpo[pnode][fproto]);
	dpo_reset (&gu->gu_dpo[pnode][fproto]);
      }
    }

    vec_foreach (gnhi, gu->gu_nhs)
    {
      fib_protocol_t fproto;

      gnh = gbp_next_hop_get (*gnhi);
      gbp_bridge_domain_unlock (gnh->gnh_bd);
      gbp_route_domain_unlock (gnh->gnh_rd);
      gbp_endpoint_child_remove (gnh->gnh_ge, gnh->gnh_sibling);
      gbp_endpoint_unlock (GBP_ENDPOINT_SRC_RR, gnh->gnh_ge);

      FOR_EACH_FIB_IP_PROTOCOL (fproto)
      {
	adj_unlock (gnh->gnh_ai[fproto]);
      }
    }
  }
  vec_free (rules);
}

static u8 *
format_gbp_next_hop (u8 * s, va_list * args)
{
  index_t gnhi = va_arg (*args, index_t);
  gbp_next_hop_t *gnh;

  gnh = gbp_next_hop_get (gnhi);

  s = format (s, "%U, %U, %U EP:%d",
	      format_mac_address_t, &gnh->gnh_mac,
	      format_gbp_bridge_domain, gnh->gnh_bd,
	      format_ip46_address, &gnh->gnh_ip, IP46_TYPE_ANY, gnh->gnh_ge);

  return (s);
}

static u8 *
format_gbp_rule_action (u8 * s, va_list * args)
{
  gbp_rule_action_t action = va_arg (*args, gbp_rule_action_t);

  switch (action)
    {
#define _(v,a) case GBP_RULE_##v: return (format (s, "%s", a));
      foreach_gbp_rule_action
#undef _
    }

  return (format (s, "unknown"));
}

static u8 *
format_gbp_hash_mode (u8 * s, va_list * args)
{
  gbp_hash_mode_t hash_mode = va_arg (*args, gbp_hash_mode_t);

  switch (hash_mode)
    {
#define _(v,a) case GBP_HASH_MODE_##v: return (format (s, "%s", a));
      foreach_gbp_hash_mode
#undef _
    }

  return (format (s, "unknown"));
}

static u8 *
format_gbp_policy_node (u8 * s, va_list * args)
{
  gbp_policy_node_t action = va_arg (*args, gbp_policy_node_t);

  switch (action)
    {
#define _(v,a) case GBP_POLICY_NODE_##v: return (format (s, "%s", a));
      foreach_gbp_policy_node
#undef _
    }

  return (format (s, "unknown"));
}

static u8 *
format_gbp_rule (u8 * s, va_list * args)
{
  index_t gui = va_arg (*args, index_t);
  gbp_policy_node_t pnode;
  fib_protocol_t fproto;
  gbp_rule_t *gu;
  index_t *gnhi;

  gu = gbp_rule_get (gui);
  s = format (s, "%U", format_gbp_rule_action, gu->gu_action);

  switch (gu->gu_action)
    {
    case GBP_RULE_PERMIT:
    case GBP_RULE_DENY:
      break;
    case GBP_RULE_REDIRECT:
      s = format (s, ", %U", format_gbp_hash_mode, gu->gu_hash_mode);
      break;
    }

  vec_foreach (gnhi, gu->gu_nhs)
  {
    s = format (s, "\n      [%U]", format_gbp_next_hop, *gnhi);
  }

  FOR_EACH_GBP_POLICY_NODE (pnode)
  {
    s = format (s, "\n    policy-%U", format_gbp_policy_node, pnode);

    FOR_EACH_FIB_IP_PROTOCOL (fproto)
    {
      if (dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
	{
	  s =
	    format (s, "\n      %U", format_dpo_id,
		    &gu->gu_dpo[pnode][fproto], 8);
	}
    }
  }

  return (s);
}

static void
gbp_contract_mk_adj (gbp_next_hop_t * gnh, fib_protocol_t fproto)
{
  ethernet_header_t *eth;
  gbp_endpoint_t *ge;
  index_t old_ai;
  u8 *rewrite;

  old_ai = gnh->gnh_ai[fproto];
  rewrite = NULL;
  vec_validate (rewrite, sizeof (*eth) - 1);
  eth = (ethernet_header_t *) rewrite;

  GBP_CONTRACT_DBG ("...mk-adj: %U", format_gbp_next_hop,
		    gnh - gbp_next_hop_pool);

  ge = gbp_endpoint_get (gnh->gnh_ge);

  eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
				     ETHERNET_TYPE_IP4 : ETHERNET_TYPE_IP6));
  mac_address_to_bytes (gbp_route_domain_get_local_mac (), eth->src_address);
  mac_address_to_bytes (&gnh->gnh_mac, eth->dst_address);

  gnh->gnh_ai[fproto] =
    adj_nbr_add_or_lock_w_rewrite (fproto,
				   fib_proto_to_link (fproto),
				   &gnh->gnh_ip, ge->ge_fwd.gef_itf, rewrite);

  adj_unlock (old_ai);
}

static flow_hash_config_t
gbp_contract_mk_lb_hp (gbp_hash_mode_t gu_hash_mode)
{
  switch (gu_hash_mode)
    {
    case GBP_HASH_MODE_SRC_IP:
      return IP_FLOW_HASH_SRC_ADDR;
    case GBP_HASH_MODE_DST_IP:
      return IP_FLOW_HASH_DST_ADDR;
    case GBP_HASH_MODE_SYMMETRIC:
      return (IP_FLOW_HASH_SRC_ADDR | IP_FLOW_HASH_DST_ADDR |
	      IP_FLOW_HASH_PROTO | IP_FLOW_HASH_SYMMETRIC);
    }

  return 0;
}

static void
gbp_contract_mk_lb (index_t gui, fib_protocol_t fproto)
{
  load_balance_path_t *paths = NULL;
  gbp_policy_node_t pnode;
  gbp_next_hop_t *gnh;
  dpo_proto_t dproto;
  gbp_rule_t *gu;
  u32 ii;

  u32 policy_nodes[] = {
    [GBP_POLICY_NODE_L2] = gbp_policy_port_node.index,
    [GBP_POLICY_NODE_IP4] = ip4_gbp_policy_dpo_node.index,
    [GBP_POLICY_NODE_IP6] = ip6_gbp_policy_dpo_node.index,
  };

  GBP_CONTRACT_DBG ("..mk-lb: %U", format_gbp_rule, gui);

  gu = gbp_rule_get (gui);
  dproto = fib_proto_to_dpo (fproto);

  if (GBP_RULE_REDIRECT != gu->gu_action)
    return;

  vec_foreach_index (ii, gu->gu_nhs)
  {
    gnh = gbp_next_hop_get (gu->gu_nhs[ii]);

    gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP4);
    gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP6);
  }

  FOR_EACH_GBP_POLICY_NODE (pnode)
  {
    vec_validate (paths, vec_len (gu->gu_nhs) - 1);

    vec_foreach_index (ii, gu->gu_nhs)
    {
      gnh = gbp_next_hop_get (gu->gu_nhs[ii]);

      paths[ii].path_index = FIB_NODE_INDEX_INVALID;
      paths[ii].path_weight = 1;
      dpo_set (&paths[ii].path_dpo, DPO_ADJACENCY,
	       dproto, gnh->gnh_ai[fproto]);
    }

    if (!dpo_id_is_valid (&gu->gu_dpo[pnode][fproto]))
      {
	dpo_id_t dpo = DPO_INVALID;

	dpo_set (&dpo, DPO_LOAD_BALANCE, dproto,
		 load_balance_create (vec_len (paths),
				      dproto,
				      gbp_contract_mk_lb_hp
				      (gu->gu_hash_mode)));
	dpo_stack_from_node (policy_nodes[pnode], &gu->gu_dpo[pnode][fproto],
			     &dpo);
	dpo_reset (&dpo);
      }

    load_balance_multipath_update (&gu->gu_dpo[pnode][fproto],
				   paths, LOAD_BALANCE_FLAG_NONE);
    vec_free (paths);
  }
}

static void
gbp_contract_mk_one_lb (index_t gui)
{
  gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP4);
  gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP6);
}

static int
gbp_contract_next_hop_resolve (index_t gui, index_t gnhi)
{
  gbp_bridge_domain_t *gbd;
  gbp_next_hop_t *gnh;
  ip46_address_t *ips;
  int rv;

  ips = NULL;
  gnh = gbp_next_hop_get (gnhi);
  gbd = gbp_bridge_domain_get (gnh->gnh_bd);

  gnh->gnh_gu = gui;
  vec_add1 (ips, gnh->gnh_ip);

  /*
   * source the endpoint this contract needs to forward via.
   * give ofrwarding details via the spine proxy. if this EP is known
   * to us, then since we source here with a low priority, the learned
   * info will take precedenc.
   */
  rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_RR,
				     gbd->gb_uu_fwd_sw_if_index,
				     ips,
				     &gnh->gnh_mac,
				     gnh->gnh_bd, gnh->gnh_rd, SCLASS_INVALID,
				     GBP_ENDPOINT_FLAG_NONE, NULL, NULL,
				     &gnh->gnh_ge);

  if (0 == rv)
    {
      gnh->gnh_sibling = gbp_endpoint_child_add (gnh->gnh_ge,
						 gbp_next_hop_fib_type, gnhi);
    }

  GBP_CONTRACT_DBG ("..resolve: %d: %d: %U", gui, gnhi, format_gbp_next_hop,
		    gnhi);

  vec_free (ips);
  return (rv);
}

static void
gbp_contract_rule_resolve (index_t gui)
{
  gbp_rule_t *gu;
  index_t *gnhi;

  gu = gbp_rule_get (gui);

  GBP_CONTRACT_DBG ("..resolve: %U", format_gbp_rule, gui);

  vec_foreach (gnhi, gu->gu_nhs)
  {
    gbp_contract_next_hop_resolve (gui, *gnhi);
  }
}

static void
gbp_contract_resolve (index_t * guis)
{
  index_t *gui;

  vec_foreach (gui, guis)
  {
    gbp_contract_rule_resolve (*gui);
  }
}

static void
gbp_contract_mk_lbs (index_t * guis)
{
  index_t *gui;

  vec_foreach (gui, guis)
  {
    gbp_contract_mk_one_lb (*gui);
  }
}

int
gbp_contract_update (sclass_t sclass,
		     sclass_t dclass,
		     u32 acl_index,
		     index_t * rules,
		     u16 * allowed_ethertypes, u32 * stats_index)
{
  gbp_main_t *gm = &gbp_main;
  u32 *acl_vec = NULL;
  gbp_contract_t *gc;
  index_t gci;
  uword *p;

  gbp_contract_key_t key = {
    .gck_src = sclass,
    .gck_dst = dclass,
  };

  if (~0 == gm->gbp_acl_user_id)
    {
      acl_plugin_exports_init (&gm->acl_plugin);
      gm->gbp_acl_user_id =
	gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg");
    }

  p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
  if (p != NULL)
    {
      gci = p[0];
      gc = gbp_contract_get (gci);
      gbp_contract_rules_free (gc->gc_rules);
      gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
      gc->gc_rules = NULL;
      vec_free (gc->gc_allowed_ethertypes);
    }
  else
    {
      pool_get_zero (gbp_contract_pool, gc);
      gc->gc_key = key;
      gci = gc - gbp_contract_pool;
      hash_set (gbp_contract_db.gc_hash, key.as_u32, gci);

      vlib_validate_combined_counter (&gbp_contract_drop_counters, gci);
      vlib_zero_combined_counter (&gbp_contract_drop_counters, gci);
      vlib_validate_combined_counter (&gbp_contract_permit_counters, gci);
      vlib_zero_combined_counter (&gbp_contract_permit_counters, gci);
    }

  GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci);

  gc->gc_rules = rules;
  gc->gc_allowed_ethertypes = allowed_ethertypes;
  gbp_contract_resolve (gc->gc_rules);
  gbp_contract_mk_lbs (gc->gc_rules);

  gc->gc_acl_index = acl_index;
  gc->gc_lc_index =
    gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id,
					     sclass, dclass);

  vec_add1 (acl_vec, gc->gc_acl_index);
  gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec);
  vec_free (acl_vec);

  *stats_index = gci;

  return (0);
}

int
gbp_contract_delete (sclass_t sclass, sclass_t dclass)
{
  gbp_contract_key_t key = {
    .gck_src = sclass,
    .gck_dst = dclass,
  };
  gbp_contract_t *gc;
  uword *p;

  p = hash_get (gbp_contract_db.gc_hash, key.as_u32);
  if (p != NULL)
    {
      gc = gbp_contract_get (p[0]);

      gbp_contract_rules_free (gc->gc_rules);
      gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index);
      vec_free (gc->gc_allowed_ethertypes);

      hash_unset (gbp_contract_db.gc_hash, key.as_u32);
      pool_put (gbp_contract_pool, gc);

      return (0);
    }

  return (VNET_API_ERROR_NO_SUCH_ENTRY);
}

void
gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
{
  gbp_contract_t *gc;

  /* *INDENT-OFF* */
  pool_foreach(gc, gbp_contract_pool,
  ({
    if (!cb(gc, ctx))
      break;
  }));
  /* *INDENT-ON* */
}

static clib_error_t *
gbp_contract_cli (vlib_main_t * vm,
		  unformat_input_t * input, vlib_cli_command_t * cmd)
{
  sclass_t sclass = SCLASS_INVALID, dclass = SCLASS_INVALID;
  u32 acl_index = ~0, stats_index;
  u8 add = 1;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "add"))
	add = 1;
      else if (unformat (input, "del"))
	add = 0;
      else if (unformat (input, "src-epg %d", &sclass))
	;
      else if (unformat (input, "dst-epg %d", &dclass))
	;
      else if (unformat (input, "acl-index %d", &acl_index))
	;
      else
	break;
    }

  if (SCLASS_INVALID == sclass)
    return clib_error_return (0, "Source EPG-ID must be specified");
  if (SCLASS_INVALID == dclass)
    return clib_error_return (0, "Destination EPG-ID must be specified");

  if (add)
    {
      gbp_contract_update (sclass, dclass, acl_index,
			   NULL, NULL, &stats_index);
    }
  else
    {
      gbp_contract_delete (sclass, dclass);
    }

  return (NULL);
}

/*?
 * Configure a GBP Contract
 *
 * @cliexpar
 * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
 * @cliexend
 ?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
{
  .path = "gbp contract",
  .short_help =
    "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
  .function = gbp_contract_cli,
};
/* *INDENT-ON* */

static u8 *
format_gbp_contract_key (u8 * s, va_list * args)
{
  gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *);

  s = format (s, "{%d,%d}", gck->gck_src, gck->gck_dst);

  return (s);
}

u8 *
format_gbp_contract (u8 * s, va_list * args)
{
  index_t gci = va_arg (*args, index_t);
  vlib_counter_t counts;
  gbp_contract_t *gc;
  index_t *gui;
  u16 *et;

  gc = gbp_contract_get (gci);

  s = format (s, "[%d] %U: acl-index:%d",
	      gci, format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index);

  vec_foreach (gui, gc->gc_rules)
  {
    s = format (s, "\n    %d: %U", *gui, format_gbp_rule, *gui);
  }

  s = format (s, "\n    allowed-ethertypes:[");
  vec_foreach (et, gc->gc_allowed_ethertypes)
  {
    int host_et = clib_net_to_host_u16 (*et);
    if (0 != host_et)
      s = format (s, "0x%x, ", host_et);
  }

  vlib_get_combined_counter (&gbp_contract_drop_counters, gci, &counts);
  s = format (s, "\n   drop:[%Ld:%Ld]", counts.packets, counts.bytes);
  vlib_get_combined_counter (&gbp_contract_permit_counters, gci, &counts);
  s = format (s, "\n   permit:[%Ld:%Ld]", counts.packets, counts.bytes);

  s = format (s, "]");

  return (s);
}

static clib_error_t *
gbp_contract_show (vlib_main_t * vm,
		   unformat_input_t * input, vlib_cli_command_t * cmd)
{
  gbp_contract_t *gc;
  u32 src, dst;
  index_t gci;

  src = dst = SCLASS_INVALID;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "src %d", &src))
	;
      else if (unformat (input, "dst %d", &dst))
	;
      else
	break;
    }

  vlib_cli_output (vm, "Contracts:");

  /* *INDENT-OFF* */
  pool_foreach (gc, gbp_contract_pool,
  ({
    gci = gc - gbp_contract_pool;

    if (SCLASS_INVALID != src && SCLASS_INVALID != dst)
      {
        if (gc->gc_key.gck_src == src &&
            gc->gc_key.gck_dst == dst)
          vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
      }
    else if (SCLASS_INVALID != src)
      {
        if (gc->gc_key.gck_src == src)
          vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
      }
    else if (SCLASS_INVALID != dst)
      {
        if (gc->gc_key.gck_dst == dst)
          vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
      }
    else
      vlib_cli_output (vm, "  %U", format_gbp_contract, gci);
  }));
  /* *INDENT-ON* */

  return (NULL);
}

/*?
 * Show Group Based Policy Contracts
 *
 * @cliexpar
 * @cliexstart{show gbp contract}
 * @cliexend
 ?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
  .path = "show gbp contract",
  .short_help = "show gbp contract [src <SRC>] [dst <DST>]\n",
  .function = gbp_contract_show,
};
/* *INDENT-ON* */

static fib_node_t *
gbp_next_hop_get_node (fib_node_index_t index)
{
  gbp_next_hop_t *gnh;

  gnh = gbp_next_hop_get (index);

  return (&gnh->gnh_node);
}

static void
gbp_next_hop_last_lock_gone (fib_node_t * node)
{
  ASSERT (0);
}

static gbp_next_hop_t *
gbp_next_hop_from_fib_node (fib_node_t * node)
{
  ASSERT (gbp_next_hop_fib_type == node->fn_type);
  return ((gbp_next_hop_t *) node);
}

static fib_node_back_walk_rc_t
gbp_next_hop_back_walk_notify (fib_node_t * node,
			       fib_node_back_walk_ctx_t * ctx)
{
  gbp_next_hop_t *gnh;

  gnh = gbp_next_hop_from_fib_node (node);

  gbp_contract_mk_one_lb (gnh->gnh_gu);

  return (FIB_NODE_BACK_WALK_CONTINUE);
}

/*
 * The FIB path's graph node virtual function table
 */
static const fib_node_vft_t gbp_next_hop_vft = {
  .fnv_get = gbp_next_hop_get_node,
  .fnv_last_lock = gbp_next_hop_last_lock_gone,
  .fnv_back_walk = gbp_next_hop_back_walk_notify,
  // .fnv_mem_show = fib_path_memory_show,
};

static clib_error_t *
gbp_contract_init (vlib_main_t * vm)
{
  gc_logger = vlib_log_register_class ("gbp", "con");
  gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft);

  return (NULL);
}

VLIB_INIT_FUNCTION (gbp_contract_init);

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