Separating renderers into features.
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / DestinationMapper.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
10
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Objects;
19 import java.util.Set;
20
21 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
25 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
26
27 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
28 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
29 import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
30 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75 import com.google.common.base.Optional;
76 import com.google.common.collect.Sets;
77
78 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
79
80 /**
81  * Manage the table that maps the destination address to the next hop
82  * for the path as well as applies any relevant routing transformations.
83  * @author readams
84  */
85 public class DestinationMapper extends FlowTable {
86     protected static final Logger LOG =
87             LoggerFactory.getLogger(DestinationMapper.class);
88
89     public static final short TABLE_ID = 2;
90     /**
91      * This is the MAC address of the magical router in the sky
92      */
93     public static final MacAddress ROUTER_MAC =
94             new MacAddress("88:f0:31:b5:12:b5");
95     public static final MacAddress MULTICAST_MAC =
96             new MacAddress("01:00:00:00:00:00");
97
98     public DestinationMapper(OfTable.OfTableCtx ctx) {
99         super(ctx);
100     }
101
102     @Override
103     public short getTableId() {
104         return TABLE_ID;
105     }
106
107     @Override
108     public void sync(ReadWriteTransaction t,
109                      InstanceIdentifier<Table> tiid,
110                      Map<String, FlowCtx> flowMap,
111                      NodeId nodeId, PolicyInfo policyInfo, Dirty dirty)
112                              throws Exception {
113         dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
114
115         HashSet<EgKey> visitedEgs = new HashSet<>();
116         HashSet<Integer> visitedFds = new HashSet<>();
117
118         for (EgKey epg : ctx.epManager.getGroupsForNode(nodeId)) {
119             Set<EgKey> peers = Sets.union(Collections.singleton(epg),
120                                           policyInfo.getPeers(epg));
121             for (EgKey peer : peers) {
122                 syncEPG(t, tiid, flowMap, nodeId,
123                         policyInfo, peer,
124                         visitedEgs, visitedFds);
125             }
126         }
127     }
128
129     // set up next-hop destinations for all the endpoints in the endpoint
130     // group on the node
131     private void syncEPG(ReadWriteTransaction t,
132                          InstanceIdentifier<Table> tiid,
133                          Map<String, FlowCtx> flowMap,
134                          NodeId nodeId, PolicyInfo policyInfo,
135                          EgKey key,
136                          HashSet<EgKey> visitedEgs,
137                          HashSet<Integer> visitedFds) throws Exception {
138         if (visitedEgs.contains(key)) return;
139         visitedEgs.add(key);
140
141         IndexedTenant tenant = ctx.policyResolver.getTenant(key.getTenantId());
142         EndpointGroup eg = tenant.getEndpointGroup(key.getEgId());
143         L2FloodDomain fd = tenant.resolveL2FloodDomain(eg.getNetworkDomain());
144         Collection<Subnet> sns = tenant.resolveSubnets(eg.getNetworkDomain());
145         L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
146         int l3Id = 0;
147
148         if (l3c != null)
149             l3Id = ctx.policyManager.getContextOrdinal(key.getTenantId(),
150                                                        l3c.getId());
151
152         Collection<Endpoint> egEps = ctx.epManager
153                 .getEndpointsForGroup(key);
154
155         for (Endpoint e : egEps) {
156             if (e.getTenant() == null || e.getEndpointGroup() == null)
157                 continue;
158             OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
159             if (ofc == null || ofc.getNodeId() == null) continue;
160
161             syncEP(t, tiid, flowMap, nodeId, policyInfo, e, ofc, tenant, key);
162         }
163
164         if (fd == null) return;
165         Integer fdId = ctx.policyManager.getContextOrdinal(key.getTenantId(),
166                                                            fd.getId());
167         if (visitedFds.contains(fdId)) return;
168         visitedFds.add(fdId);
169
170         //GroupTable must exist before we start adding flows that direct to it via fdId
171         if(groupExists(nodeId,fdId)) {
172
173             FlowId flowId = new FlowId(new StringBuilder()
174             .append("broadcast|")
175             .append(fdId).toString());
176             if (visit(flowMap, flowId.getValue())) {
177                 MatchBuilder mb = new MatchBuilder()
178                 .setEthernetMatch(new EthernetMatchBuilder()
179                 .setEthernetDestination(new EthernetDestinationBuilder()
180                 .setAddress(MULTICAST_MAC)
181                 .setMask(MULTICAST_MAC)
182                 .build())
183                 .build());
184                 addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class,Long.valueOf(fdId)));
185
186                 FlowBuilder flow = base()
187                         .setPriority(Integer.valueOf(140))
188                         .setId(flowId)
189                         .setMatch(mb.build())
190                         .setInstructions(instructions(applyActionIns(nxMoveRegTunIdAction(NxmNxReg0.class, false),
191                                 groupAction(Long.valueOf(fdId)))));
192                 writeFlow(t, tiid, flow.build());
193             }
194         }
195         for (Subnet sn : sns) {
196             writeRouterArpFlow(t, tiid, flowMap, nodeId, sn, l3Id);
197         }
198     }
199
200     private boolean groupExists(NodeId nodeId, Integer fdId) throws Exception {
201         //Fetch existing GroupTables
202         if(ctx.dataBroker==null) return false;
203
204         ReadOnlyTransaction t = ctx.dataBroker.newReadOnlyTransaction();
205         InstanceIdentifier<Node> niid = createNodePath(nodeId);
206         Optional<Node> r =
207                 t.read(LogicalDatastoreType.CONFIGURATION, niid).get();
208         if (!r.isPresent()) return false;
209         FlowCapableNode fcn = r.get().getAugmentation(FlowCapableNode.class);
210         if (fcn == null) return false;
211
212         if (fcn.getGroup() != null) {
213             for (Group g : fcn.getGroup()) {
214                 if (g.getGroupId().getValue().equals(Long.valueOf(fdId))) { //Group Exists.
215                     return true;
216                 }
217
218             }
219         }
220         return false;
221     }
222
223
224     private void writeRouterArpFlow(ReadWriteTransaction t,
225                                     InstanceIdentifier<Table> tiid,
226                                     Map<String, FlowCtx> flowMap,
227                                     NodeId nodeId,
228                                     Subnet sn,
229                                     int l3Id) {
230         if (sn != null && sn.getVirtualRouterIp() != null) {
231             if (sn.getVirtualRouterIp().getIpv4Address() != null) {
232                 String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue();
233                 FlowId flowId = new FlowId(new StringBuffer()
234                     .append("routerarp|")
235                     .append(sn.getId().getValue())
236                     .append("|")
237                     .append(ikey)
238                     .append("|")
239                     .append(l3Id)
240                     .toString());
241                 if (visit(flowMap, flowId.getValue())) {
242                     MatchBuilder mb = new MatchBuilder()
243                         .setEthernetMatch(ethernetMatch(null, null, ARP))
244                         .setLayer3Match(new ArpMatchBuilder()
245                             .setArpOp(Integer.valueOf(1))
246                             .setArpTargetTransportAddress(new Ipv4Prefix(ikey+"/32"))
247                             .build());
248                     addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
249                                                   Long.valueOf(l3Id)));
250                     BigInteger routerMac =
251                             new BigInteger(1, bytesFromHexString(ROUTER_MAC
252                                                                  .getValue()));
253                     FlowBuilder flowb = base()
254                          .setPriority(150)
255                          .setId(flowId)
256                          .setMatch(mb.build())
257                          .setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(),
258                                                                       setDlSrcAction(ROUTER_MAC),
259                                                                       nxLoadArpOpAction(BigInteger.valueOf(2L)),
260                                                                       nxMoveArpShaToArpThaAction(),
261                                                                       nxLoadArpShaAction(routerMac),
262                                                                       nxMoveArpSpaToArpTpaAction(),
263                                                                       nxLoadArpSpaAction(ikey),
264                                                                       outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT")))));
265                     writeFlow(t, tiid, flowb.build());
266                 }
267             } else {
268                 LOG.warn("IPv6 virtual router {} for subnet {} not supported",
269                          sn.getVirtualRouterIp(), sn.getId().getValue());
270             }
271         }
272     }
273
274     private void syncEP(ReadWriteTransaction t,
275                         InstanceIdentifier<Table> tiid,
276                         Map<String, FlowCtx> flowMap,
277                         NodeId nodeId, PolicyInfo policyInfo,
278                         Endpoint e, OfOverlayContext ofc,
279                         IndexedTenant tenant, EgKey key)
280                                  throws Exception {
281         ArrayList<Instruction> instructions = new ArrayList<>();
282         ArrayList<Instruction> l3instructions = new ArrayList<>();
283         List<Action> applyActions = new ArrayList<>();
284         List<Action> l3ApplyActions = new ArrayList<>();
285
286         int order = 0;
287         EndpointGroup eg = tenant.getEndpointGroup(e.getEndpointGroup());
288         L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
289         L2BridgeDomain bd = tenant.resolveL2BridgeDomain(eg.getNetworkDomain());
290
291         int egId = 0, bdId = 0, l3Id = 0, cgId = 0;
292
293         egId = ctx.policyManager.getContextOrdinal(e.getTenant(),
294                                                    e.getEndpointGroup());
295         if (bd != null)
296             bdId = ctx.policyManager.getContextOrdinal(e.getTenant(),
297                                                        bd.getId());
298         if (l3c != null)
299             l3Id = ctx.policyManager.getContextOrdinal(e.getTenant(),
300                                                        l3c.getId());
301
302         List<ConditionName> conds = ctx.epManager.getCondsForEndpoint(e);
303         ConditionGroup cg =
304                 policyInfo.getEgCondGroup(new EgKey(e.getTenant(),
305                                                     e.getEndpointGroup()),
306                                           conds);
307         cgId = ctx.policyManager.getCondGroupOrdinal(cg);
308         Action setdEPG = nxLoadRegAction(NxmNxReg2.class,
309                                          BigInteger.valueOf(egId));
310         Action setdCG = nxLoadRegAction(NxmNxReg3.class,
311                                         BigInteger.valueOf(cgId));
312         Action setNextHop;
313         String nextHop;
314         if (LocationType.External.equals(ofc.getLocationType())) {
315             // XXX - TODO - perform NAT and send to the external network
316             nextHop = "external";
317             LOG.warn("External endpoints not yet supported");
318             return;
319         } else {
320             Action setDlSrc = setDlSrcAction(ROUTER_MAC);
321             Action decTtl = decNwTtlAction();
322
323             if (Objects.equals(ofc.getNodeId(), nodeId)) {
324                 // this is a local endpoint; send to the approppriate local
325                 // port
326                 nextHop = ofc.getNodeConnectorId().getValue();
327
328                 long portNum;
329                 try {
330                     portNum = getOfPortNum(ofc.getNodeConnectorId());
331                 } catch (NumberFormatException ex) {
332                     LOG.warn("Could not parse port number {}",
333                              ofc.getNodeConnectorId(), ex);
334                     return;
335                 }
336
337                 setNextHop = nxLoadRegAction(NxmNxReg7.class,
338                                              BigInteger.valueOf(portNum));
339
340                 Action setDlDst = setDlDstAction(e.getMacAddress());
341                 l3ApplyActions.add(setDlSrc);
342                 l3ApplyActions.add(setDlDst);
343                 l3ApplyActions.add(decTtl);
344                 order +=1;
345             } else {
346                 // this endpoint is on a different switch; send to the
347                 // appropriate tunnel
348
349                 IpAddress tunDst =
350                         ctx.switchManager.getTunnelIP(ofc.getNodeId());
351                 NodeConnectorId tunPort =
352                         ctx.switchManager.getTunnelPort(nodeId);
353                 if (tunDst == null) return;
354                 if (tunPort == null) return;
355
356                 Action tundstAction;
357
358                 if (tunDst.getIpv4Address() != null) {
359                     nextHop = tunDst.getIpv4Address().getValue();
360                     tundstAction = nxLoadTunIPv4Action(nextHop, false);
361                 } else if (tunDst.getIpv6Address() != null) {
362                     // nextHop = tunDst.getIpv6Address().getValue();
363                     LOG.error("IPv6 tunnel destination {} for {} not supported",
364                               tunDst.getIpv6Address().getValue(),
365                               ofc.getNodeId());
366                     return;
367                 } else {
368                     // this shouldn't happen
369                     LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
370                     return;
371                 }
372
373
374                 long portNum;
375                 try {
376                     portNum = getOfPortNum(tunPort);
377                 } catch (NumberFormatException ex) {
378                     LOG.warn("Could not parse port number {}",
379                              ofc.getNodeConnectorId(), ex);
380                     return;
381                 }
382
383                 setNextHop = nxLoadRegAction(NxmNxReg7.class,
384                                              BigInteger.valueOf(portNum));
385                 Action tunIdAction =
386                         nxMoveRegTunIdAction(NxmNxReg0.class, false);
387
388                 applyActions.add(tunIdAction);
389                 applyActions.add(tundstAction);
390                 l3ApplyActions.add(setDlSrc);
391                 l3ApplyActions.add(decTtl);
392                 order +=1;
393             }
394         }
395         applyActions.add(setdEPG);
396         applyActions.add(setdCG);
397         applyActions.add(setNextHop);
398         Instruction applyActionsIns = new InstructionBuilder()
399             .setOrder(order++)
400             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
401             .build();
402         instructions.add(applyActionsIns);
403
404         applyActions.addAll(l3ApplyActions);
405         applyActionsIns = new InstructionBuilder()
406             .setOrder(order++)
407             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
408             .build();
409         l3instructions.add(applyActionsIns);
410
411         Instruction gotoTable = new InstructionBuilder()
412             .setOrder(order++)
413             .setInstruction(gotoTableIns((short)(getTableId()+1)))
414             .build();
415         instructions.add(gotoTable);
416         l3instructions.add(gotoTable);
417
418         FlowId flowid = new FlowId(new StringBuilder()
419             .append(bdId)
420             .append("|l2|")
421             .append(e.getMacAddress().getValue())
422             .append("|")
423             .append(nextHop)
424             .toString());
425         if (visit(flowMap, flowid.getValue())) {
426             MatchBuilder mb = new MatchBuilder()
427                 .setEthernetMatch(ethernetMatch(null,
428                                                 e.getMacAddress(),
429                                                 null));
430             addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(bdId)));
431             FlowBuilder flowb = base()
432                 .setId(flowid)
433                 .setPriority(Integer.valueOf(50))
434                 .setMatch(mb.build())
435                 .setInstructions(new InstructionsBuilder()
436                     .setInstruction(instructions)
437                     .build());
438
439             writeFlow(t, tiid, flowb.build());
440         }
441         if (e.getL3Address() == null) return;
442         for (L3Address l3a : e.getL3Address()) {
443             if (l3a.getIpAddress() == null || l3a.getL3Context() == null)
444                 continue;
445             Layer3Match m = null;
446             Long etherType = null;
447             String ikey = null;
448             if (l3a.getIpAddress().getIpv4Address() != null) {
449                 ikey = l3a.getIpAddress().getIpv4Address().getValue() + "/32";
450                 etherType = IPv4;
451                 m = new Ipv4MatchBuilder()
452                     .setIpv4Destination(new Ipv4Prefix(ikey))
453                     .build();
454             } else if (l3a.getIpAddress().getIpv6Address() != null) {
455                 ikey = l3a.getIpAddress().getIpv6Address().getValue() + "/128";
456                 etherType = IPv6;
457                 m = new Ipv6MatchBuilder()
458                     .setIpv6Destination(new Ipv6Prefix(ikey))
459                     .build();
460             } else
461                 continue;
462
463             flowid = new FlowId(new StringBuilder()
464                 .append(l3a.getL3Context().getValue())
465                 .append("|l3|")
466                 .append(ikey)
467                 .append("|")
468                 .append(nextHop)
469                 .toString());
470             if (visit(flowMap, flowid.getValue())) {
471                 MatchBuilder mb = new MatchBuilder()
472                     .setEthernetMatch(ethernetMatch(null,
473                                                     ROUTER_MAC,
474                                                     etherType))
475                     .setLayer3Match(m);
476                 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
477                                               Long.valueOf(l3Id)));
478                 FlowBuilder flowb = base()
479                     .setId(flowid)
480                     .setPriority(Integer.valueOf(132))
481                     .setMatch(mb.build())
482                     .setInstructions(new InstructionsBuilder()
483                         .setInstruction(l3instructions)
484                         .build());
485
486                 writeFlow(t, tiid, flowb.build());
487             }
488         }
489     }
490
491     static byte[] bytesFromHexString(String values) {
492         String target = "";
493         if (values != null) {
494             target = values;
495         }
496         String[] octets = target.split(":");
497
498         byte[] ret = new byte[octets.length];
499         for (int i = 0; i < octets.length; i++) {
500             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
501         }
502         return ret;
503     }
504 }