netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / lib / multipath.c
1 /*
2  * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18
19 #include "multipath.h"
20 #include <arpa/inet.h>
21 #include <inttypes.h>
22 #include <sys/types.h>
23 #include <netinet/in.h>
24 #include "dynamic-string.h"
25 #include "nx-match.h"
26 #include "ofp-actions.h"
27 #include "ofp-errors.h"
28 #include "ofp-util.h"
29 #include "openflow/nicira-ext.h"
30 #include "packets.h"
31 \f
32 /* Checks that 'mp' is valid on flow.  Returns 0 if it is valid, otherwise an
33  * OFPERR_*. */
34 enum ofperr
35 multipath_check(const struct ofpact_multipath *mp,
36                 const struct flow *flow)
37 {
38     return mf_check_dst(&mp->dst, flow);
39 }
40 \f
41 /* multipath_execute(). */
42
43 static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm,
44                                     unsigned int n_links, unsigned int arg);
45
46 /* Executes 'mp' based on the current contents of 'flow', writing the results
47  * back into 'flow'.  Sets fields in 'wc' that were used to calculate
48  * the result. */
49 void
50 multipath_execute(const struct ofpact_multipath *mp, struct flow *flow,
51                   struct flow_wildcards *wc)
52 {
53     /* Calculate value to store. */
54     uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis);
55     uint16_t link = multipath_algorithm(hash, mp->algorithm,
56                                         mp->max_link + 1, mp->arg);
57
58     flow_mask_hash_fields(flow, wc, mp->fields);
59     nxm_reg_load(&mp->dst, link, flow, wc);
60 }
61
62 static uint16_t
63 algorithm_hrw(uint32_t hash, unsigned int n_links)
64 {
65     uint32_t best_weight;
66     uint16_t best_link;
67     unsigned int link;
68
69     best_link = 0;
70     best_weight = hash_2words(hash, 0);
71     for (link = 1; link < n_links; link++) {
72         uint32_t weight = hash_2words(hash, link);
73         if (weight > best_weight) {
74             best_link = link;
75             best_weight = weight;
76         }
77     }
78     return best_link;
79 }
80
81 /* Works for 'x' in the range [1,65536], which is all we need.  */
82 static unsigned int
83 round_up_pow2(unsigned int x)
84 {
85     x--;
86     x |= x >> 1;
87     x |= x >> 2;
88     x |= x >> 4;
89     x |= x >> 8;
90     return x + 1;
91 }
92
93 static uint16_t
94 algorithm_iter_hash(uint32_t hash, unsigned int n_links, unsigned int modulo)
95 {
96     uint16_t link;
97     int i;
98
99     if (modulo < n_links || modulo / 2 > n_links) {
100         modulo = round_up_pow2(n_links);
101     }
102
103     i = 0;
104     do {
105         link = hash_2words(hash, i++) % modulo;
106     } while (link >= n_links);
107
108     return link;
109 }
110
111 static uint16_t
112 multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
113                     unsigned int n_links, unsigned int arg)
114 {
115     switch (algorithm) {
116     case NX_MP_ALG_MODULO_N:
117         return hash % n_links;
118
119     case NX_MP_ALG_HASH_THRESHOLD:
120         if (n_links == 1) {
121             return 0;
122         }
123         return hash / (UINT32_MAX / n_links + 1);
124
125     case NX_MP_ALG_HRW:
126         return (n_links <= 64
127                 ? algorithm_hrw(hash, n_links)
128                 : algorithm_iter_hash(hash, n_links, 0));
129
130     case NX_MP_ALG_ITER_HASH:
131         return algorithm_iter_hash(hash, n_links, arg);
132     }
133
134     OVS_NOT_REACHED();
135 }
136 \f
137 /* Parses 's_' as a set of arguments to the "multipath" action and initializes
138  * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
139  *
140  * Returns NULL if successful, otherwise a malloc()'d string describing the
141  * error.  The caller is responsible for freeing the returned string.*/
142 static char * OVS_WARN_UNUSED_RESULT
143 multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s)
144 {
145     char *save_ptr = NULL;
146     char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
147     char *error;
148     int n_links;
149
150     fields = strtok_r(s, ", ", &save_ptr);
151     basis = strtok_r(NULL, ", ", &save_ptr);
152     algorithm = strtok_r(NULL, ", ", &save_ptr);
153     n_links_str = strtok_r(NULL, ", ", &save_ptr);
154     arg = strtok_r(NULL, ", ", &save_ptr);
155     dst = strtok_r(NULL, ", ", &save_ptr);
156     if (!dst) {
157         return xasprintf("%s: not enough arguments to multipath action", s_);
158     }
159
160     ofpact_init_MULTIPATH(mp);
161     if (!strcasecmp(fields, "eth_src")) {
162         mp->fields = NX_HASH_FIELDS_ETH_SRC;
163     } else if (!strcasecmp(fields, "symmetric_l4")) {
164         mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
165     } else if (!strcasecmp(fields, "symmetric_l3l4")) {
166         mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4;
167     } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) {
168         mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP;
169     } else {
170         return xasprintf("%s: unknown fields `%s'", s_, fields);
171     }
172     mp->basis = atoi(basis);
173     if (!strcasecmp(algorithm, "modulo_n")) {
174         mp->algorithm = NX_MP_ALG_MODULO_N;
175     } else if (!strcasecmp(algorithm, "hash_threshold")) {
176         mp->algorithm = NX_MP_ALG_HASH_THRESHOLD;
177     } else if (!strcasecmp(algorithm, "hrw")) {
178         mp->algorithm = NX_MP_ALG_HRW;
179     } else if (!strcasecmp(algorithm, "iter_hash")) {
180         mp->algorithm = NX_MP_ALG_ITER_HASH;
181     } else {
182         return xasprintf("%s: unknown algorithm `%s'", s_, algorithm);
183     }
184     n_links = atoi(n_links_str);
185     if (n_links < 1 || n_links > 65536) {
186         return xasprintf("%s: n_links %d is not in valid range 1 to 65536",
187                          s_, n_links);
188     }
189     mp->max_link = n_links - 1;
190     mp->arg = atoi(arg);
191
192     error = mf_parse_subfield(&mp->dst, dst);
193     if (error) {
194         return error;
195     }
196     if (!mf_nxm_header(mp->dst.field->id)) {
197         return xasprintf("%s: experimenter OXM field '%s' not supported",
198                          s, dst);
199     }
200     if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
201         return xasprintf("%s: %d-bit destination field has %u possible "
202                          "values, less than specified n_links %d",
203                          s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
204     }
205
206     return NULL;
207 }
208
209 /* Parses 's_' as a set of arguments to the "multipath" action and initializes
210  * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
211  *
212  * Returns NULL if successful, otherwise a malloc()'d string describing the
213  * error.  The caller is responsible for freeing the returned string. */
214 char * OVS_WARN_UNUSED_RESULT
215 multipath_parse(struct ofpact_multipath *mp, const char *s_)
216 {
217     char *s = xstrdup(s_);
218     char *error = multipath_parse__(mp, s_, s);
219     free(s);
220     return error;
221 }
222
223 /* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
224  * describes. */
225 void
226 multipath_format(const struct ofpact_multipath *mp, struct ds *s)
227 {
228     const char *fields, *algorithm;
229
230     fields = flow_hash_fields_to_str(mp->fields);
231
232     switch (mp->algorithm) {
233     case NX_MP_ALG_MODULO_N:
234         algorithm = "modulo_n";
235         break;
236     case NX_MP_ALG_HASH_THRESHOLD:
237         algorithm = "hash_threshold";
238         break;
239     case NX_MP_ALG_HRW:
240         algorithm = "hrw";
241         break;
242     case NX_MP_ALG_ITER_HASH:
243         algorithm = "iter_hash";
244         break;
245     default:
246         algorithm = "<unknown>";
247     }
248
249     ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
250                   fields, mp->basis, algorithm, mp->max_link + 1,
251                   mp->arg);
252     mf_format_subfield(&mp->dst, s);
253     ds_put_char(s, ')');
254 }