BUG-2663: Support v6 in BGP flowspec
[bgpcep.git] / bgp / flowspec / src / main / java / org / opendaylight / protocol / bgp / flowspec / FSIpv6NlriParser.java
1 /*
2  * Copyright (c) 2015 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 package org.opendaylight.protocol.bgp.flowspec;
9
10 import com.google.common.base.Optional;
11 import com.google.common.primitives.Bytes;
12 import io.netty.buffer.ByteBuf;
13 import io.netty.buffer.Unpooled;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Set;
17 import org.opendaylight.protocol.util.BitArray;
18 import org.opendaylight.protocol.util.ByteArray;
19 import org.opendaylight.protocol.util.Ipv6Util;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Fragment;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.NumericOperand;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.Flowspec;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.FlowspecBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.FlowspecType;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCase;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.flowspec.flowspec.type.FragmentCaseBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.DestinationIpv6PrefixCase;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.DestinationIpv6PrefixCaseBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.FlowLabelCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.FlowLabelCaseBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.NextHeaderCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.NextHeaderCaseBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.SourceIpv6PrefixCase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.SourceIpv6PrefixCaseBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.flow.label._case.FlowLabel;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.flow.label._case.FlowLabelBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.next.header._case.NextHeaders;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.flowspec.destination.ipv6.flowspec.flowspec.type.next.header._case.NextHeadersBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationFlowspecIpv6Case;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationFlowspecIpv6CaseBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.destination.flowspec.ipv6._case.DestinationFlowspecBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes1;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes2;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlri;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutes;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
50 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
52 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
54
55 public final class FSIpv6NlriParser extends AbstractFSNlriParser {
56
57     private static final int NEXT_HEADER_VALUE = 3;
58     private static final int FLOW_LABLE_VALUE = 13;
59
60     static final NodeIdentifier NEXT_HEADER_NID = new NodeIdentifier(NextHeaders.QNAME);
61     static final NodeIdentifier FLOW_LABEL_NID = new NodeIdentifier(FlowLabel.QNAME);
62
63     @Override
64     protected void serializeMpReachNlri(final Attributes1 pathAttributes, final ByteBuf byteAggregator) {
65         if (pathAttributes == null) {
66             return;
67         }
68         final AdvertizedRoutes routes = (pathAttributes.getMpReachNlri()).getAdvertizedRoutes();
69         if (routes != null && routes.getDestinationType() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationFlowspecIpv6Case) {
70             final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationFlowspecIpv6Case flowspecCase = (org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationFlowspecIpv6Case) routes.getDestinationType();
71             serializeNlri(flowspecCase.getDestinationFlowspec().getFlowspec(), byteAggregator);
72         }
73     }
74
75     @Override
76     protected void serializeMpUnreachNlri(final Attributes2 pathAttributes, final ByteBuf byteAggregator) {
77         if (pathAttributes == null) {
78             return;
79         }
80         final MpUnreachNlri mpUnreachNlri = pathAttributes.getMpUnreachNlri();
81         if (mpUnreachNlri.getWithdrawnRoutes() != null && mpUnreachNlri.getWithdrawnRoutes().getDestinationType() instanceof DestinationFlowspecIpv6Case) {
82             final DestinationFlowspecIpv6Case flowspecCase = (DestinationFlowspecIpv6Case) mpUnreachNlri.getWithdrawnRoutes().getDestinationType();
83             serializeNlri(flowspecCase.getDestinationFlowspec().getFlowspec(), byteAggregator);
84         }
85     }
86
87     @Override
88     protected void serializeSpecificFSType(final FlowspecType value, final ByteBuf nlriByteBuf) {
89         if (value instanceof DestinationIpv6PrefixCase) {
90             nlriByteBuf.writeByte(DESTINATION_PREFIX_VALUE);
91             nlriByteBuf.writeBytes(insertOffsetByte(Ipv6Util.bytesForPrefixBegin(((DestinationIpv6PrefixCase) value).getDestinationPrefix())));
92         } else if (value instanceof SourceIpv6PrefixCase) {
93             nlriByteBuf.writeByte(SOURCE_PREFIX_VALUE);
94             nlriByteBuf.writeBytes(insertOffsetByte(Ipv6Util.bytesForPrefixBegin(((SourceIpv6PrefixCase) value).getSourcePrefix())));
95         } else if (value instanceof NextHeaderCase) {
96             nlriByteBuf.writeByte(NEXT_HEADER_VALUE);
97             serializeNumericOneByteValue(((NextHeaderCase) value).getNextHeaders(), nlriByteBuf);
98         } else if (value instanceof FragmentCase) {
99             nlriByteBuf.writeByte(FRAGMENT_VALUE);
100             serializeFragments(((FragmentCase) value).getFragments(), nlriByteBuf);
101         } else if (value instanceof FlowLabelCase) {
102             nlriByteBuf.writeByte(FLOW_LABLE_VALUE);
103             serializeNumericFourByteValue(((FlowLabelCase) value).getFlowLabel(), nlriByteBuf);
104         }
105     }
106
107     private static void serializeNumericFourByteValue(final List<FlowLabel> list, final ByteBuf nlriByteBuf) {
108         for (final FlowLabel item : list) {
109             final ByteBuf protoBuf = Unpooled.buffer();
110             writeShortest(item.getValue().intValue(), protoBuf);
111             serializeNumericOperand(item.getOp(), protoBuf.readableBytes(), nlriByteBuf);
112             nlriByteBuf.writeBytes(protoBuf);
113         }
114     }
115
116     private static byte[] insertOffsetByte(final byte[] ipPrefix) {
117         // income <len, prefix>
118         return Bytes.concat(new byte[] { ipPrefix[0] }, new byte[] { 0 }, ByteArray.subByte(ipPrefix, 1 , ipPrefix.length-1));
119     }
120
121     @Override
122     protected byte serializeFragment(final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.Fragment fragment) {
123         final BitArray bs = new BitArray(Byte.SIZE);
124         bs.set(DONT_FRAGMENT, Boolean.FALSE);
125         bs.set(FIRST_FRAGMENT, fragment.isFirst());
126         bs.set(IS_A_FRAGMENT, fragment.isIsA());
127         bs.set(LAST_FRAGMENT, fragment.isLast());
128         return bs.toByte();
129     }
130
131     @Override
132     DestinationType createWidthdrawnDestinationType(final List<Flowspec> dst) {
133         return new DestinationFlowspecIpv6CaseBuilder().setDestinationFlowspec(
134             new DestinationFlowspecBuilder().setFlowspec(
135                 dst).build()).build();
136     }
137
138     @Override
139     DestinationType createAdvertizedRoutesDestinationType(final List<Flowspec> dst) {
140         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationFlowspecIpv6CaseBuilder()
141         .setDestinationFlowspec(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.flowspec.rev150807.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.flowspec.ipv6._case.DestinationFlowspecBuilder()
142         .setFlowspec(dst).build()).build();
143     }
144
145     @Override
146     protected void setSpecificFlowspecType(final FlowspecBuilder builder, final short type, final ByteBuf nlri) {
147         switch (type) {
148         case DESTINATION_PREFIX_VALUE:
149             builder.setFlowspecType(new DestinationIpv6PrefixCaseBuilder().setDestinationPrefix(parseIpv6Prefix(nlri)).build());
150             break;
151         case SOURCE_PREFIX_VALUE:
152             builder.setFlowspecType(new SourceIpv6PrefixCaseBuilder().setSourcePrefix(parseIpv6Prefix(nlri)).build());
153             break;
154         case NEXT_HEADER_VALUE:
155             builder.setFlowspecType(new NextHeaderCaseBuilder().setNextHeaders(parseNextHeader(nlri)).build());
156             break;
157         case FRAGMENT_VALUE:
158             builder.setFlowspecType(new FragmentCaseBuilder().setFragments(parseFragment(nlri)).build());
159             break;
160         case FLOW_LABLE_VALUE:
161             builder.setFlowspecType(new FlowLabelCaseBuilder().setFlowLabel(parseFlowLabel(nlri)).build());
162             break;
163         default:
164             break;
165         }
166     }
167
168     private static Ipv6Prefix parseIpv6Prefix(final ByteBuf nlri) {
169         final int bitLength = nlri.readByte();
170         final int offset = nlri.readByte();
171         nlri.readBytes(offset);
172         return Ipv6Util.prefixForBytes(ByteArray.readBytes(nlri, bitLength / Byte.SIZE), bitLength);
173     }
174
175     @Override
176     protected  Fragment parseFragment(final byte fragment) {
177         final BitArray bs = BitArray.valueOf(fragment);
178         return new Fragment(Boolean.FALSE, bs.get(FIRST_FRAGMENT), bs.get(IS_A_FRAGMENT), bs.get(LAST_FRAGMENT));
179     }
180
181     private static List<NextHeaders> parseNextHeader(final ByteBuf nlri) {
182         final List<NextHeaders> headers = new ArrayList<>();
183         boolean end = false;
184         // we can do this as all fields will be rewritten in the cycle
185         final NextHeadersBuilder builder = new NextHeadersBuilder();
186         while (!end) {
187             final byte b = nlri.readByte();
188             final NumericOperand op = parseNumeric(b);
189             builder.setOp(op);
190             builder.setValue(nlri.readUnsignedByte());
191             end = op.isEndOfList();
192             headers.add(builder.build());
193         }
194         return headers;
195     }
196
197     private static List<FlowLabel> parseFlowLabel(final ByteBuf nlri) {
198         final List<FlowLabel> labels = new ArrayList<>();
199         boolean end = false;
200         // we can do this as all fields will be rewritten in the cycle
201         final FlowLabelBuilder builder = new FlowLabelBuilder();
202         while (!end) {
203             final byte b = nlri.readByte();
204             final NumericOperand op = parseNumeric(b);
205             builder.setOp(op);
206             final short length = parseLength(b);
207             builder.setValue(ByteArray.bytesToLong(ByteArray.readBytes(nlri, length)));
208             end = op.isEndOfList();
209             labels.add(builder.build());
210         }
211         return labels;
212     }
213
214     @Override
215     public void extractSpecificFlowspec(final ChoiceNode fsType, final FlowspecBuilder fsBuilder) {
216         if (fsType.getChild(DEST_PREFIX_NID).isPresent()) {
217             fsBuilder.setFlowspecType(new DestinationIpv6PrefixCaseBuilder()
218                 .setDestinationPrefix(new Ipv6Prefix((String) fsType.getChild(DEST_PREFIX_NID).get().getValue()))
219                 .build());
220         } else if (fsType.getChild(SOURCE_PREFIX_NID).isPresent()) {
221             fsBuilder.setFlowspecType(new SourceIpv6PrefixCaseBuilder()
222                 .setSourcePrefix(new Ipv6Prefix((String) fsType.getChild(SOURCE_PREFIX_NID).get().getValue()))
223                 .build());
224         } else if (fsType.getChild(NEXT_HEADER_NID).isPresent()) {
225             fsBuilder.setFlowspecType(new NextHeaderCaseBuilder().setNextHeaders(createNextHeaders((UnkeyedListNode) fsType.getChild(NEXT_HEADER_NID).get())).build());
226         } else if (fsType.getChild(FLOW_LABEL_NID).isPresent()) {
227             fsBuilder.setFlowspecType(new FlowLabelCaseBuilder().setFlowLabel(createFlowLabels((UnkeyedListNode) fsType.getChild(FLOW_LABEL_NID).get())).build());
228         }
229     }
230
231     private List<NextHeaders> createNextHeaders(final UnkeyedListNode nextHeadersData) {
232         final List<NextHeaders> nextHeaders = new ArrayList<>();
233
234         for (final UnkeyedListEntryNode node : nextHeadersData.getValue()) {
235             final NextHeadersBuilder nextHeadersBuilder = new NextHeadersBuilder();
236             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
237             if (opValue.isPresent()) {
238                 nextHeadersBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
239             }
240             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
241             if (valueNode.isPresent()) {
242                 nextHeadersBuilder.setValue((Short) valueNode.get().getValue());
243             }
244             nextHeaders.add(nextHeadersBuilder.build());
245         }
246
247         return nextHeaders;
248     }
249
250     private List<FlowLabel> createFlowLabels(final UnkeyedListNode flowLabelsData) {
251         final List<FlowLabel> flowLabels = new ArrayList<>();
252
253         for (final UnkeyedListEntryNode node : flowLabelsData.getValue()) {
254             final FlowLabelBuilder flowLabelsBuilder = new FlowLabelBuilder();
255             final Optional<DataContainerChild<? extends PathArgument, ?>> opValue = node.getChild(OP_NID);
256             if (opValue.isPresent()) {
257                 flowLabelsBuilder.setOp(createNumericOperand((Set<String>) opValue.get().getValue()));
258             }
259             final Optional<DataContainerChild<? extends PathArgument, ?>> valueNode = node.getChild(VALUE_NID);
260             if (valueNode.isPresent()) {
261                 flowLabelsBuilder.setValue((Long) valueNode.get().getValue());
262             }
263             flowLabels.add(flowLabelsBuilder.build());
264         }
265
266         return flowLabels;
267     }
268
269     @Override
270     protected void stringSpecificFSNlriType(final FlowspecType value, final StringBuilder buffer) {
271         if (value instanceof DestinationIpv6PrefixCase) {
272             buffer.append("to ");
273             buffer.append(((DestinationIpv6PrefixCase) value).getDestinationPrefix().getValue());
274         } else if (value instanceof SourceIpv6PrefixCase) {
275             buffer.append("from ");
276             buffer.append(((SourceIpv6PrefixCase) value).getSourcePrefix().getValue());
277         } else if (value instanceof NextHeaderCase) {
278             buffer.append("where next header ");
279             buffer.append(stringNumericOne(((NextHeaderCase) value).getNextHeaders()));
280         } else if (value instanceof FlowLabelCase) {
281             buffer.append("where flow label ");
282             buffer.append(stringFlowLabel(((FlowLabelCase) value).getFlowLabel()));
283         }
284     }
285
286     private static String stringFlowLabel(final List<FlowLabel> list) {
287         final StringBuilder buffer = new StringBuilder();
288         boolean isFirst = true;
289         for (final FlowLabel item : list) {
290             buffer.append(stringNumericOperand(item.getOp(), isFirst));
291             buffer.append(item.getValue());
292             buffer.append(' ');
293             if (isFirst) {
294                 isFirst = false;
295             }
296         }
297         return buffer.toString();
298     }
299
300 }