actions: Implement OVN "arp" action.
[cascardo/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 }