]> git.proxmox.com Git - ovs.git/blob - ovn/controller/pinctrl.c
actions: Implement OVN "arp" action.
[ovs.git] / ovn / controller / pinctrl.c
1 /* Copyright (c) 2015, 2016 Red Hat, Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <config.h>
17
18 #include "pinctrl.h"
19
20 #include "dirs.h"
21 #include "dp-packet.h"
22 #include "ofp-actions.h"
23 #include "ofp-msgs.h"
24 #include "ofp-print.h"
25 #include "ofp-util.h"
26 #include "ovn/lib/actions.h"
27 #include "rconn.h"
28 #include "openvswitch/vlog.h"
29 #include "socket-util.h"
30 #include "vswitch-idl.h"
31
32 VLOG_DEFINE_THIS_MODULE(pinctrl);
33
34 /* OpenFlow connection to the switch. */
35 static struct rconn *swconn;
36
37 /* Last seen sequence number for 'swconn'. When this differs from
38 * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
39 static unsigned int conn_seq_no;
40
41 void
42 pinctrl_init(void)
43 {
44 swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
45 conn_seq_no = 0;
46 }
47
48 static ovs_be32
49 queue_msg(struct ofpbuf *msg)
50 {
51 const struct ofp_header *oh = msg->data;
52 ovs_be32 xid = oh->xid;
53
54 rconn_send(swconn, msg, NULL);
55 return xid;
56 }
57
58 /* Sets up 'swconn', a newly (re)connected connection to a switch. */
59 static void
60 pinctrl_setup(struct rconn *swconn)
61 {
62 /* Fetch the switch configuration. The response later will allow us to
63 * change the miss_send_len to UINT16_MAX, so that we can enable
64 * asynchronous messages. */
65 queue_msg(ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST,
66 rconn_get_version(swconn), 0));
67
68 /* Set a packet-in format that supports userdata. */
69 queue_msg(ofputil_make_set_packet_in_format(rconn_get_version(swconn),
70 NXPIF_NXT_PACKET_IN2));
71 }
72
73 static void
74 set_switch_config(struct rconn *swconn,
75 const struct ofputil_switch_config *config)
76 {
77 enum ofp_version version = rconn_get_version(swconn);
78 struct ofpbuf *request = ofputil_encode_set_config(config, version);
79 queue_msg(request);
80 }
81
82 static void
83 pinctrl_handle_arp(const struct flow *ip_flow, struct ofpbuf *userdata)
84 {
85 /* This action only works for IP packets, and the switch should only send
86 * us IP packets this way, but check here just to be sure. */
87 if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
88 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
89 VLOG_WARN_RL(&rl, "ARP action on non-IP packet (Ethertype %"PRIx16")",
90 ntohs(ip_flow->dl_type));
91 return;
92 }
93
94 /* Compose an ARP packet. */
95 uint64_t packet_stub[128 / 8];
96 struct dp_packet packet;
97 dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
98 compose_arp__(&packet);
99
100 struct eth_header *eth = dp_packet_l2(&packet);
101 eth->eth_dst = ip_flow->dl_dst;
102 eth->eth_src = ip_flow->dl_src;
103
104 struct arp_eth_header *arp = dp_packet_l3(&packet);
105 arp->ar_op = htons(ARP_OP_REQUEST);
106 arp->ar_sha = ip_flow->dl_src;
107 put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
108 arp->ar_tha = eth_addr_zero;
109 put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
110
111 if (ip_flow->vlan_tci & htons(VLAN_CFI)) {
112 eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci);
113 }
114
115 /* Compose actions.
116 *
117 * First, add actions to restore the metadata, then add actions from
118 * 'userdata'.
119 */
120 uint64_t ofpacts_stub[1024 / 8];
121 struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
122 enum ofp_version version = rconn_get_version(swconn);
123
124 for (int id = 0; id < MFF_N_IDS; id++) {
125 const struct mf_field *field = mf_from_id(id);
126
127 if (field->prereqs == MFP_NONE
128 && field->writable
129 && id != MFF_IN_PORT && id != MFF_IN_PORT_OXM
130 && mf_is_set(field, ip_flow))
131 {
132 struct ofpact_set_field *sf = ofpact_put_SET_FIELD(&ofpacts);
133 sf->field = field;
134 sf->flow_has_vlan = false;
135 mf_get_value(field, ip_flow, &sf->value);
136 bitwise_one(&sf->mask, sizeof sf->mask, 0, field->n_bits);
137 }
138 }
139 enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
140 version, &ofpacts);
141 if (error) {
142 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
143 VLOG_WARN_RL(&rl, "failed to parse arp actions (%s)",
144 ofperr_to_string(error));
145 goto exit;
146 }
147
148 struct ofputil_packet_out po = {
149 .packet = dp_packet_data(&packet),
150 .packet_len = dp_packet_size(&packet),
151 .buffer_id = UINT32_MAX,
152 .in_port = OFPP_CONTROLLER,
153 .ofpacts = ofpacts.data,
154 .ofpacts_len = ofpacts.size,
155 };
156 enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
157 queue_msg(ofputil_encode_packet_out(&po, proto));
158
159 exit:
160 dp_packet_uninit(&packet);
161 ofpbuf_uninit(&ofpacts);
162 }
163
164 static void
165 process_packet_in(const struct ofp_header *msg)
166 {
167 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
168
169 struct ofputil_packet_in pin;
170 enum ofperr error = ofputil_decode_packet_in(msg, true, &pin,
171 NULL, NULL, NULL);
172 if (error) {
173 VLOG_WARN_RL(&rl, "error decoding packet-in: %s",
174 ofperr_to_string(error));
175 return;
176 }
177 if (pin.reason != OFPR_ACTION) {
178 return;
179 }
180
181 struct ofpbuf userdata = ofpbuf_const_initializer(pin.userdata,
182 pin.userdata_len);
183 const struct action_header *ah = ofpbuf_pull(&userdata, sizeof *ah);
184 if (!ah) {
185 VLOG_WARN_RL(&rl, "packet-in userdata lacks action header");
186 return;
187 }
188
189 struct dp_packet packet;
190 dp_packet_use_const(&packet, pin.packet, pin.packet_len);
191 struct flow headers;
192 flow_extract(&packet, &headers);
193
194 const struct flow *md = &pin.flow_metadata.flow;
195 switch (ntohl(ah->opcode)) {
196 case ACTION_OPCODE_ARP:
197 pinctrl_handle_arp(&headers, &userdata);
198 break;
199
200 default:
201 VLOG_WARN_RL(&rl, "unrecognized packet-in command %#"PRIx32,
202 md->regs[0]);
203 break;
204 }
205 }
206
207 static void
208 pinctrl_recv(const struct ofp_header *oh, enum ofptype type)
209 {
210 if (type == OFPTYPE_ECHO_REQUEST) {
211 queue_msg(make_echo_reply(oh));
212 } else if (type == OFPTYPE_GET_CONFIG_REPLY) {
213 /* Enable asynchronous messages (see "Asynchronous Messages" in
214 * DESIGN.md for more information). */
215 struct ofputil_switch_config config;
216
217 ofputil_decode_get_config_reply(oh, &config);
218 config.miss_send_len = UINT16_MAX;
219 set_switch_config(swconn, &config);
220 } else if (type == OFPTYPE_PACKET_IN) {
221 process_packet_in(oh);
222 } else if (type != OFPTYPE_ECHO_REPLY && type != OFPTYPE_BARRIER_REPLY) {
223 if (VLOG_IS_DBG_ENABLED()) {
224 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
225
226 char *s = ofp_to_string(oh, ntohs(oh->length), 2);
227
228 VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
229 free(s);
230 }
231 }
232 }
233
234 void
235 pinctrl_run(const struct ovsrec_bridge *br_int)
236 {
237 if (br_int) {
238 char *target;
239
240 target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
241 if (strcmp(target, rconn_get_target(swconn))) {
242 VLOG_INFO("%s: connecting to switch", target);
243 rconn_connect(swconn, target, target);
244 }
245 free(target);
246 } else {
247 rconn_disconnect(swconn);
248 }
249
250 rconn_run(swconn);
251
252 if (rconn_is_connected(swconn)) {
253 if (conn_seq_no != rconn_get_connection_seqno(swconn)) {
254 pinctrl_setup(swconn);
255 conn_seq_no = rconn_get_connection_seqno(swconn);
256 }
257
258 /* Process a limited number of messages per call. */
259 for (int i = 0; i < 50; i++) {
260 struct ofpbuf *msg = rconn_recv(swconn);
261 if (!msg) {
262 break;
263 }
264
265 const struct ofp_header *oh = msg->data;
266 enum ofptype type;
267
268 ofptype_decode(&type, oh);
269 pinctrl_recv(oh, type);
270 ofpbuf_delete(msg);
271 }
272 }
273 }
274
275 void
276 pinctrl_wait(void)
277 {
278 rconn_run_wait(swconn);
279 rconn_recv_wait(swconn);
280 }
281
282 void
283 pinctrl_destroy(void)
284 {
285 rconn_destroy(swconn);
286 }