OpenNSL API Guide and Reference Manual
example_policer.c
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * (C) Copyright Broadcom Corporation 2013-2017
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  **********************************************************************
18  *
19  * \file example_policer.c
20  *
21  * \brief OpenNSL example application for policer or service meter
22  *
23  * \details In this example, we will create a simple two rate, three color policer.
24  * This policer will be attached to a Field Processor rule that counts
25  * packets ingressing on a selected port with a specific VLAN Id.
26  * Packets that exceed peak rate will be colored red. A field processor
27  * stat counter will be attached that counts "red" and "not red" bytes.
28  *
29  **********************************************************************/
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <opennsl/error.h>
34 #include <opennsl/port.h>
35 #include <opennsl/vlan.h>
36 #include <opennsl/field.h>
37 #include <opennsl/policer.h>
38 #include <examples/util.h>
39 
40 #define DEFAULT_UNIT 0
41 #define DEFAULT_VLAN 1
42 
43 
46  int index, int *statId)
47 {
50  };
51 
52  /*
53  * Choose rates that don't overlap to simplify validation. Make bit
54  * rates even muliples of 8
55  * */
56  const int base_rate = (index + 1) * 50;
57  const int cir = base_rate * 8;
58  const int cbs = (base_rate + 200) * 8;
59  const int pir = (base_rate + 400) * 8;
60  const int pbs = (base_rate + 600) * 8;
61 
64  opennsl_policer_config_t policer_config;
65 
66  if (*group < 0) {
67  /* Only create group once, all entries share the same group */
69  /* Create QSET. */
71  /* Specifiy Ingress Field Processor */
73  /* Match on outer VLAN ID and Ingress Port */
76 
78  (unit, qset, OPENNSL_FIELD_GROUP_PRIO_ANY, group));
79  }
80 
81  /* Create a field processor entry */
83 
84  /* Match on Ingress port and VLAN ID */
85  OPENNSL_IF_ERROR_RETURN(opennsl_field_qualify_InPort(unit, entry, port, 0xFF));
87 
88  /* Fill out policer configuration */
89  opennsl_policer_config_t_init(&policer_config);
90  policer_config.mode = opennslPolicerModeTrTcm; /* Two rate, three color policer */
91  policer_config.flags |= OPENNSL_POLICER_COLOR_BLIND; /* Ignore previous color */
92  policer_config.ckbits_sec = cir; /* Committed Rate, below this rate, packets are green */
93  policer_config.ckbits_burst = cbs;
94  policer_config.pkbits_sec = pir; /* Peak Rate, below rate is yellow, above rate is red */
95  policer_config.pkbits_burst = pbs;
96 
97  /* Create the policer */
98  OPENNSL_IF_ERROR_RETURN(opennsl_policer_create(unit, &policer_config, &policer_id));
99 
100  /* Attach policer to the entry */
101  OPENNSL_IF_ERROR_RETURN(opennsl_field_entry_policer_attach(unit, entry, 0, policer_id));
102 
103  /* Add an FP rule to drop all red packets */
105 
106  /* Create a statistics entry to keep track of red/not-red traffic */
107  OPENNSL_IF_ERROR_RETURN(opennsl_field_stat_create(unit, *group, 2, stats, statId));
109 
110  /* Finally install the field entry */
112 
113  printf
114  (" Policer %2d, Stat: %2d; CIR: %5d[%4d]; CBS: %5d[%4d]; PIR: %5d[%4d]; PBS: %5d[%4d]\n",
115  policer_id, *statId, cir, cir / 8, cbs, cbs / 8, pir, pir / 8, pbs, pbs / 8);
116  return OPENNSL_E_NONE;
117 }
118 /* __doxy_func_body_end__ */
119 
120 
121 /* Check Policer Rates
122  *
123  * This function attempts to compute actual data rates from policers defined
124  * above. The assumption is that all ports are oversubscribed and that all
125  * policer peak rates will be exceeded resulting in traffic shaping. The
126  * computed rates are checked against expected rates. If the overall error
127  * exceeds a empirically determined limit, an error status is returned.
128  */
130 example_check_rates(int unit, int *stat_list, int stat_count)
131 {
132  const int max_error = 50; /* 1/2 of 1% */
133  const int loop_count = 15;
134  int loop;
135  int accumulated_error = 0;
136 
137  for (loop = 0; loop < loop_count; loop++) {
138  int idx;
139  const int delay = 2;
140  uint64 stat_value;
141 
142  sleep(delay);
143  printf("\nLoop: %d\n", loop);
144  for (idx = 0; idx < stat_count; idx++) {
146  (unit, stat_list[idx], opennslFieldStatNotRedBytes,
147  &stat_value));
149  (unit, stat_list[idx], opennslFieldStatNotRedBytes, 0));
150  /* First time around seems to always be off */
151  if (loop > 0) {
152  const int64 expected_rate = (((idx + 1) * 50) + 400) * 1000;
153  const int64 rate = stat_value / delay;
154  /* LSB= 0.01% */
155  int64 rate_error =
156  ((rate - expected_rate) * 10000) / expected_rate;
157 
158  printf("Rate: %d = %8llu bytes/sec [expected=%8llu, error=%lld]\n",
159  stat_list[idx], rate, expected_rate, rate_error);
160  accumulated_error += (rate_error < 0) ? -rate_error : rate_error;
161  }
162  }
163  }
164  accumulated_error /= (stat_count * (loop_count - 1));
165  if (accumulated_error > max_error) {
166  printf("FAIL: Per port avg error %d exceeds max of %d [LSB = 0.01%%]\n",
167  accumulated_error, max_error);
168  return OPENNSL_E_FAIL;
169  }
170  printf("PASS: Per port avg error: +/- %d [LSB = 0.01%%]\n", accumulated_error);
171  return OPENNSL_E_NONE;
172 }
173 /* __doxy_func_body_end__ */
174 
175 
176 /* Transmit a single test packet on the specified port */
177 static opennsl_error_t
178 example_tx_pkt(int unit, int port)
179 {
180  uint8 mypkt[] = {
181 
182 /*
183  00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15*/
184 0x01, 0x00, 0x5E, 0x00, 0x00, 0x01, 0x80, 0x80, 0x80, 0x71, 0x35, 0x42, 0x81, 0x00, 0x00, 0x01,
185 0x08, 0x00, 0x45, 0x00, 0x00, 0x5A, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, 0xD7, 0xE6, 0xC0, 0xA8,
186 0x01, 0x01, 0xE0, 0x01, 0x01, 0x01, 0x00, 0x07, 0x00, 0x07, 0x00, 0x46, 0x2C, 0xF6, 0x41, 0x6C,
187 0x6C, 0x20, 0x47, 0x6F, 0x6F, 0x64, 0x20, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x20, 0x43, 0x6F,
188 0x6D, 0x65, 0x20, 0x54, 0x6F, 0x20, 0x54, 0x68, 0x6F, 0x73, 0x65, 0x20, 0x57, 0x68, 0x6F, 0x20,
189 0x57, 0x61, 0x6E, 0x74, 0x20, 0x4C, 0x6F, 0x74, 0x73, 0x20, 0x4F, 0x66, 0x20, 0x47, 0x6F, 0x6F,
190 0x64, 0x20, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x00, 0x00, 0x00, 0x00
191  };
192  const int mypkt_SIZEOF = sizeof(mypkt);
193  opennsl_pkt_t *pkt;
194 
195  OPENNSL_IF_ERROR_RETURN(opennsl_pkt_alloc(unit, mypkt_SIZEOF, 0, &pkt));
196  opennsl_pkt_memcpy(pkt, 0, mypkt, mypkt_SIZEOF);
197  pkt->pkt_data->len = mypkt_SIZEOF;
199  OPENNSL_PBMP_PORT_SET(pkt->tx_pbmp, port);
200 
201  /* Transmit packet */
203 
205  return OPENNSL_E_NONE;
206 }
207 /* __doxy_func_body_end__ */
208 
209 
210 /* Create VLAN (if necessary) and add a port to it.
211  * Strip VLAN tags on egressing traffic.
212  */
213 static opennsl_error_t
214 example_add_port_to_vlan(int unit, opennsl_vlan_t vlan, opennsl_port_t port)
215 {
216  opennsl_pbmp_t untagged;;
217  opennsl_pbmp_t port_mask;
218  opennsl_error_t rv;
219 
220  if (OPENNSL_FAILURE(rv = opennsl_vlan_create(unit, vlan)) && (rv != OPENNSL_E_EXISTS)) {
221  printf("Failed to create VLAN %d\n", vlan);
222  return rv;
223  }
224  OPENNSL_PBMP_PORT_SET(port_mask, port);
225  OPENNSL_PBMP_PORT_SET(untagged, port);
226  OPENNSL_IF_ERROR_RETURN(opennsl_vlan_port_add(unit, vlan, port_mask, untagged));
228  return OPENNSL_E_NONE;
229 }
230 /* __doxy_func_body_end__ */
231 
232 
233 /*
234  * This function configures two ports to feed back on each other when a packet
235  * is sent on either one of them. This causes a "packet storm" on these two
236  * ports resulting in a high rate of traffic on these ports.
237  *
238  * The traffic from the storm ports is used to generate traffic on other
239  * ports. These ports will be configued such that ingressing traffic is
240  * assigned to a unique VLAN. Policers will be created for these other ports
241  * (called ingress ports).
242  */
243 int example_policer_test(int unit)
244 {
245  const opennsl_vlan_t vlan_base = 100;
246  const int max_ports = 10;
247  const int storm_ports = 2;
248 
249  opennsl_field_group_t group = -1;
250  opennsl_port_config_t portConfig;
251  opennsl_port_t ingress_list[max_ports];
253  opennsl_port_t port_list[max_ports];
254  int ingress_count;
255  int port_count;
256  int stat_ids[max_ports];
257 
258  port_count = 0;
259  ingress_count = 0;
261  OPENNSL_PBMP_ITER(portConfig.e, port) {
262  if (port_count == max_ports) {
263  break;
264  }
265  port_list[port_count] = port;
267  if (port_count >= storm_ports) {
268  opennsl_vlan_t next_vlan = (port_count >> 1) + vlan_base;
269  /*
270  * Once storm ports are configured, configure test port pairs
271  * (ingress and egress). Each port in the pair will be assigned
272  * to a VLAN. Untagged packets on these ports will be assigned
273  * to the new VLAN. The egress port only belongs to the new
274  * VLAN. The ingress port belongs to both the new VLAN and the
275  * default VLAN. This allows it to receive all traffic broadcast
276  * on the default VLAN from the storm ports. The egress port is
277  * configured to drop all packets to prevent packet storms on
278  * these port pairs.
279  */
280  OPENNSL_IF_ERROR_RETURN(example_add_port_to_vlan(unit, next_vlan, port));
281  if (port_count & 1) {
282  opennsl_pbmp_t port_mask;
283 
284  OPENNSL_PBMP_PORT_SET(port_mask, port);
285  /* Remove egress port from default VLAN */
287  } else {
288  /* Set policer on ingress port */
289  printf("Ingress Test Port: %d\n", port);
291  (unit, &group, port, next_vlan,
292  ingress_count, &stat_ids[ingress_count]));
293  ingress_list[ingress_count] = port;
294  ingress_count++;
295  }
296  }
297  port_count++;
298  }
299  if (port_count != max_ports) {
300  printf("Not enough ethernet ports for test: %d\n", port_count);
301  }
302 
303  printf("Send packet on storm port %d\n", port_list[0]);
304  /* Create a packet storm to fully subscribe all ports being tested */
305  OPENNSL_IF_ERROR_RETURN(example_tx_pkt(unit, port_list[0]));
306  OPENNSL_IF_ERROR_RETURN(example_check_rates(unit, stat_ids, ingress_count));
307 
308  return OPENNSL_E_NONE;
309 }
310 /* __doxy_func_body_end__ */
311 
312 
313 /*****************************************************************/
320 int main(int argc, char *argv[])
321 {
322  int rv = 0;
323  int unit = DEFAULT_UNIT;
324 
325  /* Initialize the system. */
327 
328  if(rv != OPENNSL_E_NONE) {
329  printf("\r\nFailed to initialize the system. Error: %s\r\n",
330  opennsl_errmsg(rv));
331  return rv;
332  }
333 
334  /* cold boot initialization commands */
335  rv = example_port_default_config(unit);
336  if (rv != OPENNSL_E_NONE) {
337  printf("\r\nFailed to apply default config on ports, rc = %d (%s).\r\n",
338  rv, opennsl_errmsg(rv));
339  }
340 
341  /* Add ports to default vlan. */
342  printf("Adding ports to default vlan.\r\n");
344  if(rv != OPENNSL_E_NONE) {
345  printf("\r\nFailed to add default ports. Error: %s\r\n",
346  opennsl_errmsg(rv));
347  }
348 
349  /* Create policer and test traffic rate */
350  rv = example_policer_test(unit);
351  if (rv != OPENNSL_E_NONE) {
352  printf("\r\nFailed to test policer. Error: %s\r\n",
353  opennsl_errmsg(rv));
354  }
355 
356  return rv;
357 }
358 /* __doxy_func_body_end__ */