347397aa338134009543b66ec7cbc9b90e1b7542
[honeycomb.git] / v3po / v3po2vpp / src / main / java / io / fd / honeycomb / translate / v3po / interfaces / acl / common / AbstractIetfAclWriter.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package io.fd.honeycomb.translate.v3po.interfaces.acl.common;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
24 import io.fd.honeycomb.translate.write.WriteContext;
25 import io.fd.honeycomb.translate.write.WriteFailedException;
26 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
27 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
28 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
29 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
30 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.ListIterator;
34 import java.util.Map;
35 import java.util.concurrent.CompletionStage;
36 import java.util.function.Predicate;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39 import javax.annotation.Nonnull;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
56 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 public abstract class AbstractIetfAclWriter implements IetfAclWriter, JvppReplyConsumer, AclTranslator {
61
62     private static final Logger LOG = LoggerFactory.getLogger(AbstractIetfAclWriter.class);
63     protected static final int NOT_DEFINED = -1;
64     protected final FutureJVppCore jvpp;
65
66     private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>();
67
68     public AbstractIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) {
69         this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
70         aceWriters.put(AclType.ETH, new AceEthWriter());
71         aceWriters.put(AclType.IP4, new AceIp4Writer());
72         aceWriters.put(AclType.IP6, new AceIp6Writer());
73         aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter());
74     }
75
76     private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
77                                               @Nonnull final WriteContext writeContext) {
78         final String aclName = assignedAcl.getName();
79         final Class<? extends AclBase> aclType = assignedAcl.getType();
80
81         // ietf-acl updates are handled first, so we use writeContext.readAfter
82         final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl>
83             aclOptional =
84             writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child(
85                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
86                 new AclKey(aclName, aclType)));
87         checkArgument(aclOptional.isPresent(), "Acl lists not configured");
88         final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
89             acl = aclOptional.get();
90
91         final AccessListEntries accessListEntries = acl.getAccessListEntries();
92         checkArgument(accessListEntries != null, "access list entries not configured");
93
94         return accessListEntries.getAce().stream();
95     }
96
97     protected void removeClassifyTables(@Nonnull final InstanceIdentifier<?> id, @Nonnull final MappingEntry entry)
98         throws WriteFailedException {
99         removeClassifyTable(id, entry.getL2TableId());
100         removeClassifyTable(id, entry.getIp4TableId());
101         removeClassifyTable(id, entry.getIp6TableId());
102     }
103
104     private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
105         throws WriteFailedException {
106
107         if (tableIndex == -1) {
108             return; // classify table id is absent
109         }
110         final ClassifyAddDelTable request = new ClassifyAddDelTable();
111         request.tableIndex = tableIndex;
112         final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
113         getReplyForDelete(cs.toCompletableFuture(), id);
114     }
115
116     protected static boolean appliesToIp4Path(final Ace ace) {
117         final AceType aceType = ace.getMatches().getAceType();
118         final AclType aclType = AclType.fromAce(ace);
119         if (aclType == AclType.IP4) {
120             return true;
121         }
122         if (aclType == AclType.ETH) {
123             return true;  // L2 only rules are possible for IP4 traffic
124         }
125         if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType)
126             .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) {
127             return true;
128         }
129         return false;
130     }
131
132     protected static boolean appliesToIp6Path(final Ace ace) {
133         final AceType aceType = ace.getMatches().getAceType();
134         final AclType aclType = AclType.fromAce(ace);
135         if (aclType == AclType.IP6) {
136             return true;
137         }
138         if (aclType == AclType.ETH) {
139             return true;  // L2 only rules are possible for IP6 traffic
140         }
141         if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType)
142             .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) {
143             return true;
144         }
145         return false;
146     }
147
148     protected static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext,
149                                        final Predicate<? super Ace> filter) {
150         return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter)
151             .collect(Collectors.toList());
152     }
153
154     protected int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces,
155                             final AccessLists.DefaultAction defaultAction, final InterfaceMode mode,
156                             final int vlanTags) throws WriteFailedException {
157         if (aces.isEmpty()) {
158             return NOT_DEFINED;
159         }
160
161         int nextTableIndex = configureDefaultAction(id, defaultAction);
162         final ListIterator<Ace> iterator = aces.listIterator(aces.size());
163         while (iterator.hasPrevious()) {
164             final Ace ace = iterator.previous();
165             LOG.trace("Processing ACE: {}", ace);
166
167             final AceWriter aceWriter =
168                 aceWriters.get(AclType.fromAce(ace));
169             if (aceWriter == null) {
170                 LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass());
171             } else {
172                 final AceType aceType = ace.getMatches().getAceType();
173                 final PacketHandling action = ace.getActions().getPacketHandling();
174                 final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags);
175                 nextTableIndex = createClassifyTable(id, ctRequest);
176                 final List<ClassifyAddDelSession> sessionRequests =
177                     aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags);
178                 for (ClassifyAddDelSession csRequest : sessionRequests) {
179                     createClassifySession(id, csRequest);
180                 }
181             }
182         }
183         return nextTableIndex;
184     }
185
186     private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id,
187                                        final AccessLists.DefaultAction defaultAction)
188         throws WriteFailedException {
189         ClassifyAddDelTable ctRequest = createTable(-1);
190         if (AccessLists.DefaultAction.Permit.equals(defaultAction)) {
191             ctRequest.missNextIndex = -1;
192         } else {
193             ctRequest.missNextIndex = 0;
194         }
195         ctRequest.mask = new byte[16];
196         ctRequest.skipNVectors = 0;
197         ctRequest.matchNVectors = 1;
198         return createClassifyTable(id, ctRequest);
199     }
200
201     private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
202                                     @Nonnull final ClassifyAddDelTable request)
203         throws WriteFailedException {
204         final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
205
206         final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id);
207         return reply.newTableIndex;
208     }
209
210     private void createClassifySession(@Nonnull final InstanceIdentifier<?> id,
211                                        @Nonnull final ClassifyAddDelSession request)
212         throws WriteFailedException {
213         final CompletionStage<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request);
214
215         getReplyForWrite(cs.toCompletableFuture(), id);
216     }
217
218     private enum AclType {
219         ETH, IP4, IP6, ETH_AND_IP;
220
221         @Nonnull
222         private static AclType fromAce(final Ace ace) {
223             AclType result = null;
224             final AceType aceType;
225             try {
226                 aceType = ace.getMatches().getAceType();
227                 if (aceType instanceof AceEth) {
228                     result = ETH;
229                 } else if (aceType instanceof AceIp) {
230                     final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion();
231                     if (aceIpVersion == null) {
232                         throw new IllegalArgumentException("Incomplete ACE (ip-version was not provided): " + ace);
233                     }
234                     if (aceIpVersion instanceof AceIpv4) {
235                         result = IP4;
236                     } else if (aceIpVersion instanceof AceIpv6) {
237                         result = IP6;
238                     }
239                 } else if (aceType instanceof AceIpAndEth) {
240                     result = ETH_AND_IP;
241                 }
242             } catch (NullPointerException e) {
243                 throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
244             }
245             if (result == null) {
246                 throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType));
247             }
248             return result;
249         }
250     }
251 }