1 # Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
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
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
19 from ryu.lib.ofctl_utils import str_to_int
20 from ryu.ofproto import nicira_ext
23 def ofp_instruction_from_str(ofproto, action_str):
25 Parse an ovs-ofctl style action string and return a list of
26 jsondict representations of OFPInstructionActions, which
27 can then be passed to ofproto_parser.ofp_instruction_from_jsondict.
29 Please note that this is for making transition from ovs-ofctl
30 easier. Please consider using OFPAction constructors when writing
33 This function takes the following arguments.
35 =========== =================================================
37 =========== =================================================
38 ofproto An ofproto module.
39 action_str An action string.
40 =========== =================================================
42 action_re = re.compile(r"([a-z_]+)(\([^)]*\)|[^a-z_,()][^,()]*)*")
44 while len(action_str):
45 m = action_re.match(action_str)
47 raise ryu.exception.OFPInvalidActionString(action_str=action_str)
48 action_name = m.group(1)
49 this_action = m.group(0)
50 paren_level = this_action.count('(') - this_action.count(')')
51 assert paren_level >= 0
53 # Parens can be nested. Look for as many ')'s as '('s.
55 this_action, rest = _tokenize_paren_block(action_str, m.end(0))
57 rest = action_str[m.end(0):]
62 raise ryu.exception.OFPInvalidActionString(action_str=action_str)
63 if action_name == 'drop':
64 assert this_action == 'drop'
65 assert len(result) == 0 and rest == ''
67 converter = getattr(OfctlActionConverter, action_name, None)
68 if converter is None or not callable(converter):
69 raise ryu.exception.OFPInvalidActionString(action_str=action_name)
70 result.append(converter(ofproto, this_action))
76 def _tokenize_paren_block(string, pos):
77 paren_re = re.compile("[()]")
78 paren_level = string[:pos].count('(') - string[:pos].count(')')
79 while paren_level > 0:
80 m = paren_re.search(string[pos:])
86 return string[:pos], string[pos:]
89 def tokenize_ofp_instruction_arg(arg):
91 Tokenize an argument portion of ovs-ofctl style action string.
93 arg_re = re.compile("[^,()]*")
98 m = arg_re.match(rest)
99 if m.end(0) == len(rest):
102 if rest[m.end(0)] == '(':
103 this_block, rest = _tokenize_paren_block(
105 result.append(this_block)
106 elif rest[m.end(0)] == ',':
107 result.append(m.group(0))
108 rest = rest[m.end(0):]
112 assert rest[0] == ','
116 raise ryu.exception.OFPInvalidActionString(action_str=arg)
119 _OXM_FIELD_OFCTL_ALIASES = {
120 'tun_id': 'tunnel_id',
121 'in_port': 'in_port_nxm',
122 'in_port_oxm': 'in_port',
124 'dl_type': 'eth_type',
125 'nw_src': 'ipv4_src',
126 'ip_src': 'ipv4_src',
127 'nw_proto': 'ip_proto',
130 'icmp_type': 'icmpv4_type',
131 'icmp_code': 'icmpv4_code',
132 'nd_target': 'ipv6_nd_target',
133 'nd_sll': 'ipv6_nd_sll',
134 'nd_tll': 'ipv6_nd_tll',
136 'tun_src': 'tun_ipv4_src'
140 def ofp_ofctl_field_name_to_ryu(field):
141 """Convert an ovs-ofctl field name to ryu equivalent."""
142 mapped = _OXM_FIELD_OFCTL_ALIASES.get(field)
145 if field.endswith("_dst"):
146 mapped = _OXM_FIELD_OFCTL_ALIASES.get(field[:-3] + "src")
148 return mapped[:-3] + "dst"
152 _NXM_FIELD_MAP = dict([(key, key + '_nxm') for key in [
153 'arp_sha', 'arp_tha', 'ipv6_src', 'ipv6_dst',
154 'icmpv6_type', 'icmpv6_code', 'ip_ecn', 'tcp_flags']])
155 _NXM_FIELD_MAP.update({
156 'tun_id': 'tunnel_id_nxm', 'ip_ttl': 'nw_ttl'})
158 _NXM_OF_FIELD_MAP = dict([(key, key + '_nxm') for key in [
159 'in_port', 'eth_dst', 'eth_src', 'eth_type', 'ip_proto',
160 'tcp_src', 'tcp_dst', 'udp_src', 'udp_dst',
161 'arp_op', 'arp_spa', 'arp_tpa']])
162 _NXM_OF_FIELD_MAP.update({
163 'ip_src': 'ipv4_src_nxm', 'ip_dst': 'ipv4_dst_nxm',
164 'icmp_type': 'icmpv4_type_nxm', 'icmp_code': 'icmpv4_code_nxm'})
167 def nxm_field_name_to_ryu(field):
169 Convert an ovs-ofctl style NXM_/OXM_ field name to
170 a ryu match field name.
172 if field.endswith("_W"):
175 field = field[7:].lower()
178 if prefix == 'NXM_NX_':
179 mapped_result = _NXM_FIELD_MAP.get(field)
180 elif prefix == "NXM_OF_":
181 mapped_result = _NXM_OF_FIELD_MAP.get(field)
182 elif prefix == "OXM_OF_":
187 if mapped_result is not None:
192 class OfctlActionConverter(object):
195 def goto_table(cls, ofproto, action_str):
196 assert action_str.startswith('goto_table:')
197 table_id = str_to_int(action_str[len('goto_table:'):])
198 return dict(OFPInstructionGotoTable={'table_id': table_id})
201 def normal(cls, ofproto, action_str):
202 return cls.output(ofproto, action_str)
205 def output(cls, ofproto, action_str):
206 if action_str == 'normal':
207 port = ofproto.OFPP_NORMAL
209 assert action_str.startswith('output:')
210 port = str_to_int(action_str[len('output:'):])
211 return dict(OFPActionOutput={'port': port})
214 def pop_vlan(cls, ofproto, action_str):
215 return dict(OFPActionPopVlan={})
218 def set_field(cls, ofproto, action_str):
220 assert action_str.startswith("set_field:")
221 value, key = action_str[len("set_field:"):].split("->", 1)
222 fieldarg = dict(field=ofp_ofctl_field_name_to_ryu(key))
225 fieldarg['value'] = str_to_int(value[:m])
226 fieldarg['mask'] = str_to_int(value[m + 1:])
228 fieldarg['value'] = str_to_int(value)
230 raise ryu.exception.OFPInvalidActionString(action_str=action_str)
231 return dict(OFPActionSetField={
232 'field': {'OXMTlv': fieldarg}})
236 def resubmit(cls, ofproto, action_str):
237 arg = action_str[len("resubmit"):]
241 kwargs['in_port'] = str_to_int(arg[1:])
242 elif arg[0] == '(' and arg[-1] == ')':
243 in_port, table_id = arg[1:-1].split(',')
245 kwargs['in_port'] = str_to_int(in_port)
247 kwargs['table_id'] = str_to_int(table_id)
250 return dict(NXActionResubmitTable=kwargs)
252 raise ryu.exception.OFPInvalidActionString(
253 action_str=action_str)
256 def conjunction(cls, ofproto, action_str):
258 assert action_str.startswith('conjunction(')
259 assert action_str[-1] == ')'
260 args = action_str[len('conjunction('):-1].split(',')
261 assert len(args) == 2
262 id_ = str_to_int(args[0])
263 clauses = list(map(str_to_int, args[1].split('/')))
264 assert len(clauses) == 2
265 return dict(NXActionConjunction={
266 'clause': clauses[0] - 1,
267 'n_clauses': clauses[1],
270 raise ryu.exception.OFPInvalidActionString(
271 action_str=action_str)
274 def ct(cls, ofproto, action_str):
275 str_to_port = {'ftp': 21, 'tftp': 69}
279 recirc_table = nicira_ext.NX_CT_RECIRC_NONE
283 if len(action_str) > 2:
284 if (not action_str.startswith('ct(') or
285 action_str[-1] != ')'):
286 raise ryu.exception.OFPInvalidActionString(
287 action_str=action_str)
288 rest = tokenize_ofp_instruction_arg(action_str[len('ct('):-1])
293 flags |= nicira_ext.NX_CT_F_COMMIT
294 rest = rest[len('commit'):]
296 flags |= nicira_ext.NX_CT_F_FORCE
297 elif arg.startswith('exec('):
298 ct_actions = ofp_instruction_from_str(
299 ofproto, arg[len('exec('):-1])
302 k, v = arg.split('=', 1)
304 recirc_table = str_to_int(v)
306 m = re.search(r'\[(\d*)\.\.(\d*)\]', v)
308 zone_ofs_nbits = nicira_ext.ofs_nbits(
309 int(m.group(1)), int(m.group(2)))
310 zone_src = nxm_field_name_to_ryu(
313 zone_ofs_nbits = str_to_int(v)
315 alg = str_to_port[arg[len('alg='):]]
317 raise ryu.exception.OFPInvalidActionString(
318 action_str=action_str)
319 return dict(NXActionCT={'flags': flags,
320 'zone_src': zone_src,
321 'zone_ofs_nbits': zone_ofs_nbits,
322 'recirc_table': recirc_table,
324 'actions': ct_actions})
327 def ct_clear(cls, ofproto, action_str):
328 return dict(NXActionCTClear={})