1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. |
---|
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 |
---|
12 | # implied. |
---|
13 | # See the License for the specific language governing permissions and |
---|
14 | # limitations under the License. |
---|
15 | |
---|
16 | from ryu.base import app_manager |
---|
17 | from ryu.controller import ofp_event |
---|
18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER |
---|
19 | from ryu.controller.handler import set_ev_cls |
---|
20 | from ryu.ofproto import ofproto_v1_3 |
---|
21 | from ryu.lib.packet import packet |
---|
22 | from ryu.lib.packet import ethernet |
---|
23 | from ryu.lib.packet import ether_types |
---|
24 | |
---|
25 | pi1_ip4_add = '10.10.1.4' |
---|
26 | pi2_ip4_add = '10.10.1.5' |
---|
27 | pi3_ip4_add = '10.10.1.6' |
---|
28 | |
---|
29 | pi1_eth_add = 'b8:27:eb:21:14:0d' |
---|
30 | pi2_eth_add = 'b8:27:eb:43:97:10' |
---|
31 | pi3_eth_add = 'b8:27:eb:23:c1:0c' |
---|
32 | |
---|
33 | insert_special_flow = True |
---|
34 | |
---|
35 | class SimpleSwitch13(app_manager.RyuApp): |
---|
36 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] |
---|
37 | |
---|
38 | def __init__(self, *args, **kwargs): |
---|
39 | super(SimpleSwitch13, self).__init__(*args, **kwargs) |
---|
40 | self.mac_to_port = {} |
---|
41 | |
---|
42 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) |
---|
43 | def switch_features_handler(self, ev): |
---|
44 | datapath = ev.msg.datapath |
---|
45 | ofproto = datapath.ofproto |
---|
46 | parser = datapath.ofproto_parser |
---|
47 | |
---|
48 | # install table-miss flow entry |
---|
49 | # |
---|
50 | # We specify NO BUFFER to max_len of the output action due to |
---|
51 | # OVS bug. At this moment, if we specify a lesser number, e.g., |
---|
52 | # 128, OVS will send Packet-In with invalid buffer_id and |
---|
53 | # truncated packet data. In that case, we cannot output packets |
---|
54 | # correctly. The bug has been fixed in OVS v2.1.0. |
---|
55 | match = parser.OFPMatch() |
---|
56 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, |
---|
57 | ofproto.OFPCML_NO_BUFFER)] |
---|
58 | self.add_flow(datapath, 0, match, actions) |
---|
59 | |
---|
60 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): |
---|
61 | ofproto = datapath.ofproto |
---|
62 | parser = datapath.ofproto_parser |
---|
63 | |
---|
64 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, |
---|
65 | actions)] |
---|
66 | if buffer_id: |
---|
67 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, |
---|
68 | priority=priority, match=match, |
---|
69 | instructions=inst) |
---|
70 | else: |
---|
71 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, |
---|
72 | match=match, instructions=inst) |
---|
73 | datapath.send_msg(mod) |
---|
74 | |
---|
75 | def get_eth_add_from_port(self, dpid, port): |
---|
76 | for each in self.mac_to_port[dpid]: |
---|
77 | print(str(self.mac_to_port[dpid][each]) + "\n\n\n\n") |
---|
78 | if self.mac_to_port[dpid][each] == port: |
---|
79 | return each |
---|
80 | |
---|
81 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) |
---|
82 | def _packet_in_handler(self, ev): |
---|
83 | # If you hit this you might want to increase |
---|
84 | # the "miss_send_length" of your switch |
---|
85 | if ev.msg.msg_len < ev.msg.total_len: |
---|
86 | self.logger.debug("packet truncated: only %s of %s bytes", |
---|
87 | ev.msg.msg_len, ev.msg.total_len) |
---|
88 | msg = ev.msg |
---|
89 | datapath = msg.datapath |
---|
90 | ofproto = datapath.ofproto |
---|
91 | parser = datapath.ofproto_parser |
---|
92 | in_port = msg.match['in_port'] |
---|
93 | |
---|
94 | pkt = packet.Packet(msg.data) |
---|
95 | eth = pkt.get_protocols(ethernet.ethernet)[0] |
---|
96 | |
---|
97 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: |
---|
98 | # ignore lldp packet |
---|
99 | return |
---|
100 | dst = eth.dst |
---|
101 | src = eth.src |
---|
102 | |
---|
103 | dpid = datapath.id |
---|
104 | self.mac_to_port.setdefault(dpid, {}) |
---|
105 | |
---|
106 | # learn a mac address to avoid FLOOD next time. |
---|
107 | self.mac_to_port[dpid][src] = in_port |
---|
108 | |
---|
109 | if dst in self.mac_to_port[dpid]: |
---|
110 | out_port = self.mac_to_port[dpid][dst] |
---|
111 | else: |
---|
112 | out_port = ofproto.OFPP_FLOOD |
---|
113 | |
---|
114 | actions = [parser.OFPActionOutput(out_port)] |
---|
115 | |
---|
116 | self.logger.info("packet in %s %s %s %s->%s", dpid, src, dst, in_port, out_port) |
---|
117 | |
---|
118 | # Duplicate a packet to go out on port 2 as well |
---|
119 | if insert_special_flow: |
---|
120 | if out_port == 3 and not in_port == 2: |
---|
121 | actions = [parser.OFPActionOutput(out_port), |
---|
122 | parser.OFPActionSetField(eth_dst=pi3_eth_add), |
---|
123 | parser.OFPActionSetField(ipv4_dst=pi3_ip4_add), |
---|
124 | parser.OFPActionOutput(2)] |
---|
125 | |
---|
126 | # install a flow to avoid packet_in next time |
---|
127 | if out_port != ofproto.OFPP_FLOOD: |
---|
128 | if insert_special_flow and out_port == 3 and not in_port == 2: |
---|
129 | match = parser.OFPMatch(in_port=in_port, eth_type=0x0800) |
---|
130 | else: |
---|
131 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) |
---|
132 | # verify if we have a valid buffer_id, if yes avoid to send both |
---|
133 | # flow_mod & packet_out |
---|
134 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: |
---|
135 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) |
---|
136 | return |
---|
137 | else: |
---|
138 | self.add_flow(datapath, 1, match, actions) |
---|
139 | data = None |
---|
140 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: |
---|
141 | data = msg.data |
---|
142 | |
---|
143 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, |
---|
144 | in_port=in_port, actions=actions, data=data) |
---|
145 | datapath.send_msg(out) |
---|