aboutsummaryrefslogtreecommitdiffstats
path: root/src/VppUplink.cpp
blob: fcaddf7326bf37daf8bdfb3285ef3629cb5956fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
/*
 * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

#include <opflexagent/logging.h>

#include "vom/arp_proxy_binding.hpp"
#include "vom/arp_proxy_config.hpp"
#include "vom/bond_interface.hpp"
#include "vom/bond_member.hpp"
#include "vom/interface.hpp"
#include "vom/ip_punt_redirect.hpp"
#include "vom/ip_unnumbered.hpp"
#include "vom/l3_binding.hpp"
#include "vom/lldp_binding.hpp"
#include "vom/lldp_global.hpp"
#include "vom/neighbour.hpp"
#include "vom/sub_interface.hpp"
#include <vom/bond_group_binding.hpp>
#include <vom/qos_mark.hpp>
#include <vom/qos_map.hpp>
#include <vom/qos_record.hpp>

#include "VppSpineProxy.hpp"
#include "VppUplink.hpp"
#include "VppUtil.hpp"

using namespace VOM;

namespace VPP
{

/**
 * THe DSCP and VLAN CoS value opflex wants for its control packets
 */
const static u8 opflex_cp_dscp = 5;

static const std::string UPLINK_KEY = "__uplink__";

Uplink::Uplink(opflexagent::Agent &agent)
    : m_type(VLAN)
    , m_agent(agent)
{
}

const std::string &
Uplink::system_name() const
{
    return m_system_name;
}

std::shared_ptr<VOM::interface>
Uplink::mk_interface(const std::string &uuid, uint32_t vnid)
{
    std::shared_ptr<VOM::interface> sp;
    switch (m_type)
    {
    case VXLAN:
    {
        vxlan_tunnel vt(m_vxlan.src, m_vxlan.dst, vnid);
        VOM::OM::write(uuid, vt);

        return vt.singular();
    }
    case VLAN:
    {
        sub_interface sb(*m_uplink, interface::admin_state_t::UP, vnid);
        VOM::OM::write(uuid, sb);

        return sb.singular();
    }
    }

    return sp;
}

void
Uplink::configure_tap(const route::prefix_t &pfx)
{

    /**
     * Create a tap interface with a fixed mac so we can add a
     * ARP entry for it
     */
    mac_address_t tap_mac("00:00:de:ad:be:ef");

    tap_interface itf("tap0", interface::admin_state_t::UP, pfx, tap_mac);
    VOM::OM::write(UPLINK_KEY, itf);

    neighbour::flags_t f =
        (neighbour::flags_t::STATIC | neighbour::flags_t::NO_FIB_ENTRY);

    neighbour tap_nbr(itf, pfx.address(), tap_mac, f);
    VOM::OM::write(UPLINK_KEY, tap_nbr);

    /*
     * commit and L3 Config to the OM so this uplink owns the
     * subnet on the interface. If we don't have a representation
     * of the configured prefix in the OM, we'll sweep it from the
     * interface if we restart
     */
    l3_binding l3(*m_subitf, pfx);
    OM::commit(UPLINK_KEY, l3);

    ip_unnumbered ipUnnumber(itf, *m_subitf);
    VOM::OM::write(UPLINK_KEY, ipUnnumber);

    arp_proxy_config arpProxyConfig(pfx.low().address().to_v4(),
                                    pfx.high().address().to_v4());
    VOM::OM::write(UPLINK_KEY, arpProxyConfig);

    arp_proxy_binding arpProxyBinding(itf);
    VOM::OM::write(UPLINK_KEY, arpProxyBinding);

    ip_punt_redirect ipPunt(*m_subitf, itf, pfx.address());
    VOM::OM::write(UPLINK_KEY, ipPunt);

    /**
     * record the QoS bits for packets from the TAP
     */
    QoS::record qr(itf, QoS::source_t::IP);
    OM::write(UPLINK_KEY, qr);
}

void
Uplink::handle_dhcp_event(std::shared_ptr<VOM::dhcp_client::lease_t> lease)
{
    m_agent.getAgentIOService().dispatch(
        bind(&Uplink::handle_dhcp_event_i, this, lease));
}

void
Uplink::handle_dhcp_event_i(std::shared_ptr<dhcp_client::lease_t> lease)
{
    LOG(opflexagent::INFO) << "DHCP Event: " << lease->to_string();

    m_pfx = lease->host_prefix;

    /*
     * Create the TAP interface with the DHCP learn address.
     *  This allows all traffic punt to VPP to arrive at the TAP/agent.
     */
    configure_tap(m_pfx);

    /*
     * VXLAN tunnels use the DHCP address as the source
     */
    m_vxlan.src = m_pfx.address();
}

std::shared_ptr<SpineProxy>
Uplink::spine_proxy()
{
    switch (m_agent.getRendererForwardingMode())
    {
    case opflex::ofcore::OFConstants::STITCHED_MODE:
        break;
    case opflex::ofcore::OFConstants::TRANSPORT_MODE:
    {
        boost::asio::ip::address_v4 v4, v6, mac;

        m_agent.getV4Proxy(v4);
        m_agent.getV6Proxy(v6);
        m_agent.getMacProxy(mac);

        return std::make_shared<SpineProxy>(
            local_address().to_v4(), v4, v6, mac);
        break;
    }
    }
    return {};
}

const boost::asio::ip::address &
Uplink::local_address() const
{
    return m_pfx.address();
}

const std::string
Uplink::uplink_l2_address() const
{
    const std::string str("");
    if (m_uplink)
    {
        return m_uplink->l2_address().to_string();
    }
    return str;
}

const std::shared_ptr<interface>
Uplink::local_interface() const
{
    return m_subitf;
}

void
Uplink::configure(const std::string &fqdn)
{
    m_system_name = fqdn;

    LOG(opflexagent::INFO) << "configure:" << m_system_name;

    /*
     * Consruct the uplink physical, so we now 'own' it
     */
    VOM::interface::type_t type = getIntfTypeFromName(m_iface);
    if (VOM::interface::type_t::BOND == type)
    {
        bond_interface bitf(m_iface,
                            interface::admin_state_t::UP,
                            bond_interface::mode_t::LACP,
                            bond_interface::lb_t::L2);
        OM::write(UPLINK_KEY, bitf);
        bond_group_binding::enslaved_itf_t slave_itfs;
        for (auto sif : slave_ifaces)
        {
            interface sitf(
                sif, getIntfTypeFromName(sif), interface::admin_state_t::UP);
            OM::write(UPLINK_KEY, sitf);
            bond_member bm(
                sitf, bond_member::mode_t::ACTIVE, bond_member::rate_t::SLOW);
            slave_itfs.insert(bm);
        }
        if (!slave_itfs.empty())
        {
            bond_group_binding bgb(bitf, slave_itfs);
            OM::write(UPLINK_KEY, bgb);
        }
        m_uplink = bitf.singular();
    }
    else
    {
        interface itf(m_iface, type, interface::admin_state_t::UP);
        OM::write(UPLINK_KEY, itf);
        m_uplink = itf.singular();
    }

    /*
     * Own the v4 and v6 global tables
     */
    route_domain v4_gbl(0);
    OM::write(UPLINK_KEY, v4_gbl);
    route_domain v6_gbl(0);
    OM::write(UPLINK_KEY, v6_gbl);

    /**
     * Enable LLDP on this uplionk
     */
    lldp_global lg(m_system_name, 5, 2);
    OM::write(UPLINK_KEY, lg);
    lldp_binding lb(*m_uplink, "uplink-interface");
    OM::write(UPLINK_KEY, lb);

    /*
     * now create the sub-interface on which control and data traffic from
     * the upstream leaf will arrive
     */
    sub_interface subitf(*m_uplink, interface::admin_state_t::UP, m_vlan);
    OM::write(UPLINK_KEY, subitf);
    m_subitf = subitf.singular();

    /**
     * Strip the fully qualified domain name of any domain name
     * to get just the hostname.
     */
    std::string hostname = fqdn;
    std::string::size_type n = hostname.find(".");
    if (n != std::string::npos)
    {
        hostname = hostname.substr(0, n);
    }

    /**
     * Configure DHCP on the uplink subinterface
     * We must use the MAC address of the uplink interface as the DHCP client-ID
     */
    dhcp_client dc(*m_subitf, hostname, m_uplink->l2_address(), true,
                   ip_dscp_t(opflex_cp_dscp), this);
    OM::write(UPLINK_KEY, dc);

    /**
     * In the case of a agent restart, the DHCP process will already be complete
     * in VPP and we won't get notified. So check here if the DHCP lease
     * is already aquired.
     */
    std::shared_ptr<dhcp_client::lease_t> lease = dc.singular()->lease();

    if (lease && lease->state != dhcp_client::state_t::DISCOVER)
    {
        LOG(opflexagent::INFO) << "DHCP present: " << lease->to_string();
        configure_tap(lease->host_prefix);
        m_vxlan.src = lease->host_prefix.address();
        m_pfx = lease->host_prefix;
    }
    else
    {
        LOG(opflexagent::DEBUG) << "DHCP awaiting lease";
    }

    /**
     * set up the QoS marking for CP packets - VLAN CoS=5
     */
    QoS::map::outputs_t outputs;

    for (auto &x : outputs)
        for (auto &y : x)
            y = opflex_cp_dscp;

    QoS::map qem(1, outputs);
    OM::write(UPLINK_KEY, qem);

    QoS::mark qm(*m_subitf, qem, QoS::source_t::IP);
    OM::write(UPLINK_KEY, qm);
}

void
Uplink::set(const std::string &uplink,
            uint16_t uplink_vlan,
            const std::string &encap_name,
            const boost::asio::ip::address &remote_ip,
            uint16_t port)
{
    m_type = VXLAN;
    m_vxlan.dst = remote_ip;
    m_iface = uplink;
    m_vlan = uplink_vlan;
}

void
Uplink::set(const std::string &uplink,
            uint16_t uplink_vlan,
            const std::string &encap_name)
{
    m_type = VLAN;
    m_iface = uplink;
    m_vlan = uplink_vlan;
}

void
Uplink::insert_slave_ifaces(std::string name)
{
    this->slave_ifaces.insert(name);
}

void
Uplink::insert_dhcp_options(std::string name)
{
    this->dhcp_options.insert(name);
}
} // namespace VPP

/*
 * Local Variables:
 * eval: (c-set-style "llvm.org")
 * End:
 */