1 /* Copyright (c) 2015, 2016 Red Hat, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "dp-packet.h"
22 #include "ofp-actions.h"
24 #include "ofp-print.h"
26 #include "ovn/lib/actions.h"
28 #include "openvswitch/vlog.h"
29 #include "socket-util.h"
30 #include "vswitch-idl.h"
32 VLOG_DEFINE_THIS_MODULE(pinctrl);
34 /* OpenFlow connection to the switch. */
35 static struct rconn *swconn;
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;
44 swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
49 queue_msg(struct ofpbuf *msg)
51 const struct ofp_header *oh = msg->data;
52 ovs_be32 xid = oh->xid;
54 rconn_send(swconn, msg, NULL);
58 /* Sets up 'swconn', a newly (re)connected connection to a switch. */
60 pinctrl_setup(struct rconn *swconn)
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));
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));
74 set_switch_config(struct rconn *swconn,
75 const struct ofputil_switch_config *config)
77 enum ofp_version version = rconn_get_version(swconn);
78 struct ofpbuf *request = ofputil_encode_set_config(config, version);
83 pinctrl_handle_arp(const struct flow *ip_flow, struct ofpbuf *userdata)
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));
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);
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;
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);
111 if (ip_flow->vlan_tci & htons(VLAN_CFI)) {
112 eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci);
117 * First, add actions to restore the metadata, then add actions from
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);
124 for (int id = 0; id < MFF_N_IDS; id++) {
125 const struct mf_field *field = mf_from_id(id);
127 if (field->prereqs == MFP_NONE
129 && id != MFF_IN_PORT && id != MFF_IN_PORT_OXM
130 && mf_is_set(field, ip_flow))
132 struct ofpact_set_field *sf = ofpact_put_SET_FIELD(&ofpacts);
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);
139 enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
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));
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,
156 enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
157 queue_msg(ofputil_encode_packet_out(&po, proto));
160 dp_packet_uninit(&packet);
161 ofpbuf_uninit(&ofpacts);
165 process_packet_in(const struct ofp_header *msg)
167 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
169 struct ofputil_packet_in pin;
170 enum ofperr error = ofputil_decode_packet_in(msg, true, &pin,
173 VLOG_WARN_RL(&rl, "error decoding packet-in: %s",
174 ofperr_to_string(error));
177 if (pin.reason != OFPR_ACTION) {
181 struct ofpbuf userdata = ofpbuf_const_initializer(pin.userdata,
183 const struct action_header *ah = ofpbuf_pull(&userdata, sizeof *ah);
185 VLOG_WARN_RL(&rl, "packet-in userdata lacks action header");
189 struct dp_packet packet;
190 dp_packet_use_const(&packet, pin.packet, pin.packet_len);
192 flow_extract(&packet, &headers);
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);
201 VLOG_WARN_RL(&rl, "unrecognized packet-in command %#"PRIx32,
208 pinctrl_recv(const struct ofp_header *oh, enum ofptype type)
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;
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);
226 char *s = ofp_to_string(oh, ntohs(oh->length), 2);
228 VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
235 pinctrl_run(const struct ovsrec_bridge *br_int)
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);
247 rconn_disconnect(swconn);
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);
258 /* Process a limited number of messages per call. */
259 for (int i = 0; i < 50; i++) {
260 struct ofpbuf *msg = rconn_recv(swconn);
265 const struct ofp_header *oh = msg->data;
268 ofptype_decode(&type, oh);
269 pinctrl_recv(oh, type);
278 rconn_run_wait(swconn);
279 rconn_recv_wait(swconn);
283 pinctrl_destroy(void)
285 rconn_destroy(swconn);