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
);