]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #1654 from mkanjari/evpn-symm-routing-enhancements
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 8 Feb 2018 10:46:29 +0000 (11:46 +0100)
committerGitHub <noreply@github.com>
Thu, 8 Feb 2018 10:46:29 +0000 (11:46 +0100)
Evpn symmetric routing enhancements

100 files changed:
bgpd/bgp_evpn.c
bgpd/bgp_io.c
bgpd/bgp_io.h
bgpd/bgp_keepalives.c
bgpd/bgp_keepalives.h
bgpd/bgp_nht.c
bgpd/bgp_packet.c
bgpd/bgp_route.h
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/rfapi/rfapi_vty.c
configure.ac
debianpkg/backports/ubuntu12.04/debian/rules
debianpkg/backports/ubuntu14.04/debian/rules
debianpkg/rules
doc/OSPF-SR.rst [new file with mode: 0644]
doc/bgpd.texi
doc/ospfd.texi
doc/vnc.texi
isisd/isis_circuit.c
isisd/isis_lsp.c
isisd/isis_tlvs.c
lib/command.c
lib/command.h
lib/command_graph.c
lib/command_lex.l
lib/defun_lex.l
lib/frr_pthread.c
lib/frr_pthread.h
lib/if.c
lib/keychain.c
lib/mpls.h
lib/prefix.h
lib/ptm_lib.c
lib/spf_backoff.c
lib/vty.c
lib/zclient.c
lib/zclient.h
ospf6d/ospf6_area.c
ospf6d/ospf6_area.h
ospf6d/ospf6_asbr.c
ospf6d/ospf6_asbr.h
ospf6d/ospf6_lsa.c
ospf6d/ospf6_lsa.h
ospf6d/ospf6_main.c
ospf6d/ospf6_memory.c
ospf6d/ospf6_memory.h
ospf6d/ospf6_spf.c
ospf6d/ospf6_spf.h
ospf6d/ospf6_top.c
ospf6d/ospf6_top.h
ospf6d/ospf6d.c
ospfd/ospf_dump.c
ospfd/ospf_dump.h
ospfd/ospf_ext.c [new file with mode: 0644]
ospfd/ospf_ext.h [new file with mode: 0644]
ospfd/ospf_interface.c
ospfd/ospf_interface.h
ospfd/ospf_memory.c
ospfd/ospf_memory.h
ospfd/ospf_opaque.c
ospfd/ospf_opaque.h
ospfd/ospf_ri.c
ospfd/ospf_ri.h
ospfd/ospf_spf.c
ospfd/ospf_sr.c [new file with mode: 0644]
ospfd/ospf_sr.h [new file with mode: 0644]
ospfd/ospf_te.c
ospfd/ospf_te.h
ospfd/ospf_vty.c
ospfd/ospfd.c
ospfd/ospfd.h
ospfd/subdir.am
pimd/pim_cmd.c
pimd/pim_instance.c
pimd/pim_nht.c
pimd/pim_vty.c
pimd/pim_vty.h
pimd/pim_zebra.c
tests/bgpd/test_aspath.c
tests/bgpd/test_capability.c
tests/bgpd/test_mp_attr.c
tools/checkpatch.pl [new file with mode: 0755]
tools/checkpatch.sh [new file with mode: 0755]
tools/start-stop-daemon.c
vtysh/Makefile.am
vtysh/vtysh.c
zebra/kernel_socket.c
zebra/rt.h
zebra/rt_netlink.c
zebra/rt_socket.c
zebra/zebra_mpls.h
zebra/zebra_ptm.c
zebra/zebra_rib.c
zebra/zebra_rnh.c
zebra/zebra_routemap.c
zebra/zebra_vrf.c
zebra/zebra_vty.c

index 7724a0eec89c579a8733be4b7a8e8dedf7bae112..c7d5f8d1118c785b30704f475dfec5e0afb75594 100644 (file)
@@ -99,19 +99,8 @@ static unsigned int vrf_import_rt_hash_key_make(void *p)
 {
        struct vrf_irt_node *irt = p;
        char *pnt = irt->rt.val;
-       unsigned int key = 0;
-       int c = 0;
-
-       key += pnt[c];
-       key += pnt[c + 1];
-       key += pnt[c + 2];
-       key += pnt[c + 3];
-       key += pnt[c + 4];
-       key += pnt[c + 5];
-       key += pnt[c + 6];
-       key += pnt[c + 7];
-
-       return key;
+
+       return jhash(pnt, 8, 0x5abc1234);
 }
 
 /*
@@ -224,19 +213,8 @@ static unsigned int import_rt_hash_key_make(void *p)
 {
        struct irt_node *irt = p;
        char *pnt = irt->rt.val;
-       unsigned int key = 0;
-       int c = 0;
-
-       key += pnt[c];
-       key += pnt[c + 1];
-       key += pnt[c + 2];
-       key += pnt[c + 3];
-       key += pnt[c + 4];
-       key += pnt[c + 5];
-       key += pnt[c + 6];
-       key += pnt[c + 7];
-
-       return (key);
+
+       return jhash(pnt, 8, 0xdeadbeef);
 }
 
 /*
index f4bfc90b7e37adb8b7e0c719b6d736cfc95e0479..59b2d1cdaa49c35c07fc614889c11206ca99f186 100644 (file)
@@ -51,100 +51,12 @@ static bool validate_header(struct peer *);
 #define BGP_IO_TRANS_ERR (1 << 0) // EAGAIN or similar occurred
 #define BGP_IO_FATAL_ERR (1 << 1) // some kind of fatal TCP error
 
-/* Plumbing & control variables for thread lifecycle
- * ------------------------------------------------------------------------ */
-bool bgp_io_thread_run;
-pthread_mutex_t *running_cond_mtx;
-pthread_cond_t *running_cond;
-
-/* Unused callback for thread_add_read() */
-static int bgp_io_dummy(struct thread *thread) { return 0; }
-
-/* Poison pill task */
-static int bgp_io_finish(struct thread *thread)
-{
-       bgp_io_thread_run = false;
-       return 0;
-}
-
-/* Extern lifecycle control functions. init -> start -> stop
- * ------------------------------------------------------------------------ */
-void bgp_io_init()
-{
-       bgp_io_thread_run = false;
-
-       running_cond_mtx = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
-       running_cond = XCALLOC(MTYPE_PTHREAD_PRIM, sizeof(pthread_cond_t));
-
-       pthread_mutex_init(running_cond_mtx, NULL);
-       pthread_cond_init(running_cond, NULL);
-
-       /* unlocked in bgp_io_wait_running() */
-       pthread_mutex_lock(running_cond_mtx);
-}
-
-void *bgp_io_start(void *arg)
-{
-       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
-       fpt->master->owner = pthread_self();
-
-       // fd so we can sleep in poll()
-       int sleeper[2];
-       pipe(sleeper);
-       thread_add_read(fpt->master, &bgp_io_dummy, NULL, sleeper[0], NULL);
-
-       // we definitely don't want to handle signals
-       fpt->master->handle_signals = false;
-
-       struct thread task;
-
-       pthread_mutex_lock(running_cond_mtx);
-       {
-               bgp_io_thread_run = true;
-               pthread_cond_signal(running_cond);
-       }
-       pthread_mutex_unlock(running_cond_mtx);
-
-       while (bgp_io_thread_run) {
-               if (thread_fetch(fpt->master, &task)) {
-                       thread_call(&task);
-               }
-       }
-
-       close(sleeper[1]);
-       close(sleeper[0]);
-
-       return NULL;
-}
-
-void bgp_io_wait_running()
-{
-       while (!bgp_io_thread_run)
-               pthread_cond_wait(running_cond, running_cond_mtx);
-
-       /* locked in bgp_io_init() */
-       pthread_mutex_unlock(running_cond_mtx);
-}
-
-int bgp_io_stop(void **result, struct frr_pthread *fpt)
-{
-       thread_add_event(fpt->master, &bgp_io_finish, NULL, 0, NULL);
-       pthread_join(fpt->thread, result);
-
-       pthread_mutex_destroy(running_cond_mtx);
-       pthread_cond_destroy(running_cond);
-
-       XFREE(MTYPE_PTHREAD_PRIM, running_cond_mtx);
-       XFREE(MTYPE_PTHREAD_PRIM, running_cond);
-
-       return 0;
-}
-
-/* Extern API -------------------------------------------------------------- */
+/* Thread external API ----------------------------------------------------- */
 
 void bgp_writes_on(struct peer *peer)
 {
-       assert(bgp_io_thread_run);
+       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+       assert(fpt->running);
 
        assert(peer->status != Deleted);
        assert(peer->obuf);
@@ -154,8 +66,6 @@ void bgp_writes_on(struct peer *peer)
        assert(!peer->t_connect_check_w);
        assert(peer->fd);
 
-       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
-
        thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd,
                         &peer->t_write);
        SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
@@ -163,9 +73,8 @@ void bgp_writes_on(struct peer *peer)
 
 void bgp_writes_off(struct peer *peer)
 {
-       assert(bgp_io_thread_run);
-
        struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+       assert(fpt->running);
 
        thread_cancel_async(fpt->master, &peer->t_write, NULL);
        THREAD_OFF(peer->t_generate_updgrp_packets);
@@ -175,7 +84,8 @@ void bgp_writes_off(struct peer *peer)
 
 void bgp_reads_on(struct peer *peer)
 {
-       assert(bgp_io_thread_run);
+       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+       assert(fpt->running);
 
        assert(peer->status != Deleted);
        assert(peer->ibuf);
@@ -186,8 +96,6 @@ void bgp_reads_on(struct peer *peer)
        assert(!peer->t_connect_check_w);
        assert(peer->fd);
 
-       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
-
        thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
                        &peer->t_read);
 
@@ -196,9 +104,8 @@ void bgp_reads_on(struct peer *peer)
 
 void bgp_reads_off(struct peer *peer)
 {
-       assert(bgp_io_thread_run);
-
        struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+       assert(fpt->running);
 
        thread_cancel_async(fpt->master, &peer->t_read, NULL);
        THREAD_OFF(peer->t_process_packet);
@@ -206,9 +113,9 @@ void bgp_reads_off(struct peer *peer)
        UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
 }
 
-/* Internal functions ------------------------------------------------------- */
+/* Thread internal functions ----------------------------------------------- */
 
-/**
+/*
  * Called from I/O pthread when a file descriptor has become ready for writing.
  */
 static int bgp_process_writes(struct thread *thread)
@@ -231,11 +138,13 @@ static int bgp_process_writes(struct thread *thread)
        }
        pthread_mutex_unlock(&peer->io_mtx);
 
-       if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { /* no problem */
+       /* no problem */
+       if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
        }
 
+       /* problem */
        if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
-               reschedule = false; /* problem */
+               reschedule = false;
                fatal = true;
        }
 
@@ -250,7 +159,7 @@ static int bgp_process_writes(struct thread *thread)
        return 0;
 }
 
-/**
+/*
  * Called from I/O pthread when a file descriptor has become ready for reading,
  * or has hung up.
  *
@@ -321,8 +230,10 @@ static int bgp_process_reads(struct thread *thread)
                /* if this fails we are seriously screwed */
                assert(pktsize <= BGP_MAX_PACKET_SIZE);
 
-               /* If we have that much data, chuck it into its own
-                * stream and append to input queue for processing. */
+               /*
+                * If we have that much data, chuck it into its own
+                * stream and append to input queue for processing.
+                */
                if (ringbuf_remain(ibw) >= pktsize) {
                        struct stream *pkt = stream_new(pktsize);
                        assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize);
@@ -356,7 +267,7 @@ static int bgp_process_reads(struct thread *thread)
        return 0;
 }
 
-/**
+/*
  * Flush peer output buffer.
  *
  * This function pops packets off of peer->obuf and writes them to peer->fd.
@@ -379,7 +290,6 @@ static uint16_t bgp_write(struct peer *peer)
        uint16_t status = 0;
        uint32_t wpkt_quanta_old;
 
-       // cache current write quanta
        wpkt_quanta_old =
            atomic_load_explicit(&peer->bgp->wpkt_quanta, memory_order_relaxed);
 
@@ -398,7 +308,7 @@ static uint16_t bgp_write(struct peer *peer)
                                }
 
                                goto done;
-                       } else if (num != writenum) // incomplete write
+                       } else if (num != writenum)
                                stream_forward_getp(s, num);
 
                } while (num != writenum);
@@ -427,8 +337,10 @@ static uint16_t bgp_write(struct peer *peer)
                        if (peer->v_start >= (60 * 2))
                                peer->v_start = (60 * 2);
 
-                       /* Handle Graceful Restart case where the state changes
-                        * to Connect instead of Idle */
+                       /*
+                        * Handle Graceful Restart case where the state changes
+                        * to Connect instead of Idle.
+                        */
                        BGP_EVENT_ADD(peer, BGP_Stop);
                        goto done;
 
@@ -472,7 +384,7 @@ done : {
        return status;
 }
 
-/**
+/*
  * Reads a chunk of data from peer->fd into peer->ibuf_work.
  *
  * @return status flag (see top-of-file)
index 7cfd1db7106982e50bfa6d56daa366bcc25d5f91..14a12d3705b977dc2395e35cea293ac3f5db91fa 100644 (file)
 #include "bgpd/bgpd.h"
 #include "frr_pthread.h"
 
-/**
- * Initializes data structures and flags for the write thread.
- *
- * This function must be called from the main thread before
- * bgp_writes_start() is invoked.
- */
-extern void bgp_io_init(void);
-
 /**
  * Start function for write thread.
  *
@@ -43,15 +35,6 @@ extern void bgp_io_init(void);
  */
 extern void *bgp_io_start(void *arg);
 
-/**
- * Wait until the IO thread is ready to accept jobs.
- *
- * This function must be called immediately after the thread has been created
- * for running. Use of other functions before calling this one will result in
- * undefined behavior.
- */
-extern void bgp_io_wait_running(void);
-
 /**
  * Start function for write thread.
  *
index afa280a799ff7db01569228a465ebb217c2e0490..5a48c7013e63d55425cc7d9d94e76abc3afc263f 100644 (file)
 #include "bgpd/bgp_keepalives.h"
 /* clang-format on */
 
-/**
+/*
  * Peer KeepAlive Timer.
  * Associates a peer with the time of its last keepalive.
  */
 struct pkat {
-       // the peer to send keepalives to
+       /* the peer to send keepalives to */
        struct peer *peer;
-       // absolute time of last keepalive sent
+       /* absolute time of last keepalive sent */
        struct timeval last;
 };
 
@@ -52,9 +52,6 @@ static pthread_mutex_t *peerhash_mtx;
 static pthread_cond_t *peerhash_cond;
 static struct hash *peerhash;
 
-/* Thread control flag. */
-bool bgp_keepalives_thread_run = false;
-
 static struct pkat *pkat_new(struct peer *peer)
 {
        struct pkat *pkat = XMALLOC(MTYPE_TMP, sizeof(struct pkat));
@@ -100,10 +97,10 @@ static void peer_process(struct hash_backet *hb, void *arg)
 
        static struct timeval tolerance = {0, 100000};
 
-       // calculate elapsed time since last keepalive
+       /* calculate elapsed time since last keepalive */
        monotime_since(&pkat->last, &elapsed);
 
-       // calculate difference between elapsed time and configured time
+       /* calculate difference between elapsed time and configured time */
        ka.tv_sec = pkat->peer->v_keepalive;
        timersub(&ka, &elapsed, &diff);
 
@@ -118,10 +115,10 @@ static void peer_process(struct hash_backet *hb, void *arg)
                bgp_keepalive_send(pkat->peer);
                monotime(&pkat->last);
                memset(&elapsed, 0x00, sizeof(struct timeval));
-               diff = ka; // time until next keepalive == peer keepalive time
+               diff = ka;
        }
 
-       // if calculated next update for this peer < current delay, use it
+       /* if calculated next update for this peer < current delay, use it */
        if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
                *next_update = diff;
 }
@@ -139,29 +136,9 @@ static unsigned int peer_hash_key(void *arg)
        return (uintptr_t)pkat->peer;
 }
 
-void bgp_keepalives_init()
-{
-       peerhash_mtx = XCALLOC(MTYPE_TMP, sizeof(pthread_mutex_t));
-       peerhash_cond = XCALLOC(MTYPE_TMP, sizeof(pthread_cond_t));
-
-       // initialize mutex
-       pthread_mutex_init(peerhash_mtx, NULL);
-
-       // use monotonic clock with condition variable
-       pthread_condattr_t attrs;
-       pthread_condattr_init(&attrs);
-       pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
-       pthread_cond_init(peerhash_cond, &attrs);
-       pthread_condattr_destroy(&attrs);
-
-       // initialize peer hashtable
-       peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL);
-}
-
+/* Cleanup handler / deinitializer. */
 static void bgp_keepalives_finish(void *arg)
 {
-       bgp_keepalives_thread_run = false;
-
        if (peerhash) {
                hash_clean(peerhash, pkat_del);
                hash_free(peerhash);
@@ -177,32 +154,50 @@ static void bgp_keepalives_finish(void *arg)
        XFREE(MTYPE_TMP, peerhash_cond);
 }
 
-/**
+/*
  * Entry function for peer keepalive generation pthread.
- *
- * bgp_keepalives_init() must be called prior to this.
  */
 void *bgp_keepalives_start(void *arg)
 {
+       struct frr_pthread *fpt = arg;
+       fpt->master->owner = pthread_self();
+
        struct timeval currtime = {0, 0};
        struct timeval aftertime = {0, 0};
        struct timeval next_update = {0, 0};
        struct timespec next_update_ts = {0, 0};
 
+       peerhash_mtx = XCALLOC(MTYPE_TMP, sizeof(pthread_mutex_t));
+       peerhash_cond = XCALLOC(MTYPE_TMP, sizeof(pthread_cond_t));
+
+       /* initialize mutex */
+       pthread_mutex_init(peerhash_mtx, NULL);
+
+       /* use monotonic clock with condition variable */
+       pthread_condattr_t attrs;
+       pthread_condattr_init(&attrs);
+       pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+       pthread_cond_init(peerhash_cond, &attrs);
+       pthread_condattr_destroy(&attrs);
+
+       /* initialize peer hashtable */
+       peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL);
        pthread_mutex_lock(peerhash_mtx);
 
-       // register cleanup handler
+       /* register cleanup handler */
        pthread_cleanup_push(&bgp_keepalives_finish, NULL);
 
-       bgp_keepalives_thread_run = true;
+       /* notify anybody waiting on us that we are done starting up */
+       frr_pthread_notify_running(fpt);
 
-       while (bgp_keepalives_thread_run) {
+       while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
                if (peerhash->count > 0)
                        pthread_cond_timedwait(peerhash_cond, peerhash_mtx,
                                               &next_update_ts);
                else
                        while (peerhash->count == 0
-                              && bgp_keepalives_thread_run)
+                              && atomic_load_explicit(&fpt->running,
+                                                      memory_order_relaxed))
                                pthread_cond_wait(peerhash_cond, peerhash_mtx);
 
                monotime(&currtime);
@@ -219,7 +214,7 @@ void *bgp_keepalives_start(void *arg)
                TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts);
        }
 
-       // clean up
+       /* clean up */
        pthread_cleanup_pop(1);
 
        return NULL;
@@ -229,6 +224,12 @@ void *bgp_keepalives_start(void *arg)
 
 void bgp_keepalives_on(struct peer *peer)
 {
+       if (CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+               return;
+
+       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_KEEPALIVES);
+       assert(fpt->running);
+
        /* placeholder bucket data to use for fast key lookups */
        static struct pkat holder = {0};
 
@@ -253,6 +254,12 @@ void bgp_keepalives_on(struct peer *peer)
 
 void bgp_keepalives_off(struct peer *peer)
 {
+       if (!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+               return;
+
+       struct frr_pthread *fpt = frr_pthread_get(PTHREAD_KEEPALIVES);
+       assert(fpt->running);
+
        /* placeholder bucket data to use for fast key lookups */
        static struct pkat holder = {0};
 
@@ -283,10 +290,13 @@ void bgp_keepalives_wake()
        pthread_mutex_unlock(peerhash_mtx);
 }
 
-int bgp_keepalives_stop(void **result, struct frr_pthread *fpt)
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result)
 {
-       bgp_keepalives_thread_run = false;
+       assert(fpt->running);
+
+       atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
        bgp_keepalives_wake();
+
        pthread_join(fpt->thread, result);
        return 0;
 }
index 1fbd035b9edd5d7ccea5387ed7c7b514f73fd051..d1cb7d2462ac5c3cea0f6eac52e1ff6aebe882da 100644 (file)
@@ -88,6 +88,6 @@ extern void bgp_keepalives_wake(void);
 /**
  * Stops the thread and blocks until it terminates.
  */
-int bgp_keepalives_stop(void **result, struct frr_pthread *fpt);
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result);
 
 #endif /* _FRR_BGP_KEEPALIVES_H */
index 247884d2948b15fe7457d7efe42161b35d9ba5ba..d39fbec86dedffa88d5543308c9ffc9e868b6804 100644 (file)
@@ -320,18 +320,15 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer)
 
 void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
 {
-       struct stream *s;
        struct bgp_node *rn = NULL;
        struct bgp_nexthop_cache *bnc;
        struct nexthop *nexthop;
        struct nexthop *oldnh;
        struct nexthop *nhlist_head = NULL;
        struct nexthop *nhlist_tail = NULL;
-       uint32_t metric;
-       u_char nexthop_num;
-       struct prefix p;
        int i;
        struct bgp *bgp;
+       struct zapi_route nhr;
 
        bgp = bgp_lookup_by_vrf_id(vrf_id);
        if (!bgp) {
@@ -341,33 +338,26 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                return;
        }
 
-       s = zclient->ibuf;
-
-       memset(&p, 0, sizeof(struct prefix));
-       p.family = stream_getw(s);
-       p.prefixlen = stream_getc(s);
-       switch (p.family) {
-       case AF_INET:
-               p.u.prefix4.s_addr = stream_get_ipv4(s);
-               break;
-       case AF_INET6:
-               stream_get(&p.u.prefix6, s, 16);
-               break;
-       default:
-               break;
+       if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+               if (BGP_DEBUG(nht, NHT))
+                       zlog_debug("%s: Failure to decode nexthop update",
+                                  __PRETTY_FUNCTION__);
+               return;
        }
 
        if (command == ZEBRA_NEXTHOP_UPDATE)
                rn = bgp_node_lookup(
-                       bgp->nexthop_cache_table[family2afi(p.family)], &p);
+                       bgp->nexthop_cache_table[family2afi(nhr.prefix.family)],
+                       &nhr.prefix);
        else if (command == ZEBRA_IMPORT_CHECK_UPDATE)
                rn = bgp_node_lookup(
-                       bgp->import_check_table[family2afi(p.family)], &p);
+                       bgp->import_check_table[family2afi(nhr.prefix.family)],
+                       &nhr.prefix);
 
        if (!rn || !rn->info) {
                if (BGP_DEBUG(nht, NHT)) {
                        char buf[PREFIX2STR_BUFFER];
-                       prefix2str(&p, buf, sizeof(buf));
+                       prefix2str(&nhr.prefix, buf, sizeof(buf));
                        zlog_debug("parse nexthop update(%s): rn not found",
                                   buf);
                }
@@ -380,62 +370,34 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
        bgp_unlock_node(rn);
        bnc->last_update = bgp_clock();
        bnc->change_flags = 0;
-       stream_getc(s); // Distance but not currently used
-       metric = stream_getl(s);
-       nexthop_num = stream_getc(s);
 
        /* debug print the input */
        if (BGP_DEBUG(nht, NHT)) {
                char buf[PREFIX2STR_BUFFER];
-               prefix2str(&p, buf, sizeof(buf));
+               prefix2str(&nhr.prefix, buf, sizeof(buf));
                zlog_debug(
                        "%u: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x",
-                       vrf_id, buf, metric, bnc->metric, nexthop_num,
+                       vrf_id, buf, nhr.metric, bnc->metric, nhr.nexthop_num,
                        bnc->nexthop_num, bnc->flags);
        }
 
-       if (metric != bnc->metric)
+       if (nhr.metric != bnc->metric)
                bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED;
 
-       if (nexthop_num != bnc->nexthop_num)
+       if (nhr.nexthop_num != bnc->nexthop_num)
                bnc->change_flags |= BGP_NEXTHOP_CHANGED;
 
-       if (nexthop_num) {
+       if (nhr.nexthop_num) {
                /* notify bgp fsm if nbr ip goes from invalid->valid */
                if (!bnc->nexthop_num)
                        UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
 
                bnc->flags |= BGP_NEXTHOP_VALID;
-               bnc->metric = metric;
-               bnc->nexthop_num = nexthop_num;
-
-               for (i = 0; i < nexthop_num; i++) {
-                       nexthop = nexthop_new();
-                       nexthop->type = stream_getc(s);
-                       switch (nexthop->type) {
-                       case NEXTHOP_TYPE_IPV4:
-                               nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
-                       case NEXTHOP_TYPE_IFINDEX:
-                               nexthop->ifindex = stream_getl(s);
-                               break;
-                       case NEXTHOP_TYPE_IPV4_IFINDEX:
-                               nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
-                       case NEXTHOP_TYPE_IPV6:
-                               stream_get(&nexthop->gate.ipv6, s, 16);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
-                       case NEXTHOP_TYPE_IPV6_IFINDEX:
-                               stream_get(&nexthop->gate.ipv6, s, 16);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
-                       default:
-                               /* do nothing */
-                               break;
-                       }
+               bnc->metric = nhr.metric;
+               bnc->nexthop_num = nhr.nexthop_num;
+
+               for (i = 0; i < nhr.nexthop_num; i++) {
+                       nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
 
                        if (BGP_DEBUG(nht, NHT)) {
                                char buf[NEXTHOP_STRLEN];
@@ -470,7 +432,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
                bnc->nexthop = nhlist_head;
        } else {
                bnc->flags &= ~BGP_NEXTHOP_VALID;
-               bnc->nexthop_num = nexthop_num;
+               bnc->nexthop_num = nhr.nexthop_num;
 
                /* notify bgp fsm if nbr ip goes from valid->invalid */
                UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
@@ -572,12 +534,11 @@ static int make_prefix(int afi, struct bgp_info *ri, struct prefix *p)
  */
 static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command)
 {
-       struct stream *s;
        struct prefix *p;
+       bool exact_match = false;
        int ret;
 
-       /* Check socket. */
-       if (!zclient || zclient->sock < 0)
+       if (!zclient)
                return;
 
        /* Don't try to register if Zebra doesn't know of this instance. */
@@ -585,32 +546,14 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command)
                return;
 
        p = &(bnc->node->p);
-       s = zclient->obuf;
-       stream_reset(s);
-       zclient_create_header(s, command, bnc->bgp->vrf_id);
        if ((command == ZEBRA_NEXTHOP_REGISTER ||
             command == ZEBRA_IMPORT_ROUTE_REGISTER) &&
            (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)
             || CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)))
-               stream_putc(s, 1);
-       else
-               stream_putc(s, 0);
-
-       stream_putw(s, PREFIX_FAMILY(p));
-       stream_putc(s, p->prefixlen);
-       switch (PREFIX_FAMILY(p)) {
-       case AF_INET:
-               stream_put_in_addr(s, &p->u.prefix4);
-               break;
-       case AF_INET6:
-               stream_put(s, &(p->u.prefix6), 16);
-               break;
-       default:
-               break;
-       }
-       stream_putw_at(s, 0, stream_get_endp(s));
+               exact_match = true;
 
-       ret = zclient_send_message(zclient);
+       ret = zclient_send_rnh(zclient, command, p,
+                              exact_match, bnc->bgp->vrf_id);
        /* TBD: handle the failure */
        if (ret < 0)
                zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
index eed5fdc65d2362b0c68862e4d1ad949095cfc706..0ce2466f52aa9e9f97b9ca411034df528aad1248 100644 (file)
@@ -545,19 +545,26 @@ void bgp_open_send(struct peer *peer)
        bgp_writes_on(peer);
 }
 
-/* This is only for sending NOTIFICATION message to neighbor. */
+/*
+ * Writes NOTIFICATION message directly to a peer socket without waiting for
+ * the I/O thread.
+ *
+ * There must be exactly one stream on the peer->obuf FIFO, and the data within
+ * this stream must match the format of a BGP NOTIFICATION message.
+ * Transmission is best-effort.
+ *
+ * @requires peer->io_mtx
+ * @param peer
+ * @return 0
+ */
 static int bgp_write_notify(struct peer *peer)
 {
        int ret, val;
        u_char type;
        struct stream *s;
 
-       pthread_mutex_lock(&peer->io_mtx);
-       {
-               /* There should be at least one packet. */
-               s = stream_fifo_pop(peer->obuf);
-       }
-       pthread_mutex_unlock(&peer->io_mtx);
+       /* There should be at least one packet. */
+       s = stream_fifo_pop(peer->obuf);
 
        if (!s)
                return 0;
@@ -622,6 +629,14 @@ static int bgp_write_notify(struct peer *peer)
  * This function attempts to write the packet from the thread it is called
  * from, to ensure the packet gets out ASAP.
  *
+ * This function may be called from multiple threads. Since the function
+ * modifies I/O buffer(s) in the peer, these are locked for the duration of the
+ * call to prevent tampering from other threads.
+ *
+ * Delivery of the NOTIFICATION is attempted once and is best-effort. After
+ * return, the peer structure *must* be reset; no assumptions about session
+ * state are valid.
+ *
  * @param peer
  * @param code      BGP error code
  * @param sub_code  BGP error subcode
@@ -634,6 +649,10 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code,
        struct stream *s;
        int length;
 
+       /* Lock I/O mutex to prevent other threads from pushing packets */
+       pthread_mutex_lock(&peer->io_mtx);
+       /* ============================================== */
+
        /* Allocate new stream. */
        s = stream_new(BGP_MAX_PACKET_SIZE);
 
@@ -651,20 +670,8 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code,
        /* Set BGP packet length. */
        length = bgp_packet_set_size(s);
 
-       /*
-        * Turn off keepalive generation for peer. This is necessary because
-        * otherwise between the time we wipe the output buffer and the time we
-        * push the NOTIFY onto it, the KA generation thread could have pushed
-        * a KEEPALIVE in the middle.
-        */
-       bgp_keepalives_off(peer);
-
        /* wipe output buffer */
-       pthread_mutex_lock(&peer->io_mtx);
-       {
-               stream_fifo_clean(peer->obuf);
-       }
-       pthread_mutex_unlock(&peer->io_mtx);
+       stream_fifo_clean(peer->obuf);
 
        /*
         * If possible, store last packet for debugging purposes. This check is
@@ -728,9 +735,12 @@ void bgp_notify_send_with_data(struct peer *peer, u_char code, u_char sub_code,
                peer->last_reset = PEER_DOWN_NOTIFY_SEND;
 
        /* Add packet to peer's output queue */
-       bgp_packet_add(peer, s);
+       stream_fifo_push(peer->obuf, s);
 
        bgp_write_notify(peer);
+
+       /* ============================================== */
+       pthread_mutex_unlock(&peer->io_mtx);
 }
 
 /*
index cd4d027c6e6409b59bbcecef87168782683d6b03..2d4034d77dcbfa178439f36ef7549077934279f7 100644 (file)
@@ -421,8 +421,6 @@ extern void bgp_peer_clear_node_queue_drain_immediate(struct peer *peer);
 extern void bgp_process_queues_drain_immediate(void);
 
 /* for encap/vpn */
-extern struct bgp_node *bgp_afi_node_get(struct bgp_table *, afi_t, safi_t,
-                                        struct prefix *, struct prefix_rd *);
 extern struct bgp_node *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi,
                                            safi_t safi, struct prefix *p,
                                            struct prefix_rd *prd);
index d3edc899853d390c140825e1beee22777cfbcace..4a8eeb9121447b54fafd3cb465224038fed0388c 100644 (file)
@@ -2698,7 +2698,7 @@ DEFUN (bgp_default_shutdown,
        NO_STR
        BGP_STR
        "Configure BGP defaults\n"
-       "Do not automatically activate peers upon configuration\n")
+       "Apply administrative shutdown to newly configured peers\n")
 {
        VTY_DECLVAR_CONTEXT(bgp, bgp);
        bgp->autoshutdown = !strmatch(argv[0]->text, "no");
@@ -3415,6 +3415,18 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str,
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
+       /*
+        * If 'neighbor <interface>', then this is for directly connected peers,
+        * we should not accept disable-connected-check.
+        */
+       if (peer->conf_if && (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)) {
+               vty_out(vty,
+                       "%s is directly connected peer, cannot accept disable-"
+                       "connected-check\n",
+                       ip_str);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        if (!set && flag == PEER_FLAG_SHUTDOWN)
                peer_tx_shutdown_message_unset(peer);
 
@@ -4505,9 +4517,9 @@ DEFUN (no_neighbor_ebgp_multihop,
 /* disable-connected-check */
 DEFUN (neighbor_disable_connected_check,
        neighbor_disable_connected_check_cmd,
-       "neighbor <A.B.C.D|X:X::X:X> <disable-connected-check|enforce-multihop>",
+       "neighbor <A.B.C.D|X:X::X:X|WORD> <disable-connected-check|enforce-multihop>",
        NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR
+       NEIGHBOR_ADDR_STR2
        "one-hop away EBGP peer using loopback address\n"
        "Enforce EBGP neighbors perform multihop\n")
 {
@@ -4518,10 +4530,10 @@ DEFUN (neighbor_disable_connected_check,
 
 DEFUN (no_neighbor_disable_connected_check,
        no_neighbor_disable_connected_check_cmd,
-       "no neighbor <A.B.C.D|X:X::X:X> <disable-connected-check|enforce-multihop>",
+       "no neighbor <A.B.C.D|X:X::X:X|WORD> <disable-connected-check|enforce-multihop>",
        NO_STR
        NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR
+       NEIGHBOR_ADDR_STR2
        "one-hop away EBGP peer using loopback address\n"
        "Enforce EBGP neighbors perform multihop\n")
 {
@@ -5960,9 +5972,9 @@ ALIAS_HIDDEN(
 
 DEFUN (neighbor_ttl_security,
        neighbor_ttl_security_cmd,
-       "neighbor <A.B.C.D|X:X::X:X> ttl-security hops (1-254)",
+       "neighbor <A.B.C.D|X:X::X:X|WORD> ttl-security hops (1-254)",
        NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR
+       NEIGHBOR_ADDR_STR2
        "BGP ttl-security parameters\n"
        "Specify the maximum number of hops to the BGP peer\n"
        "Number of hops to BGP peer\n")
@@ -5978,15 +5990,26 @@ DEFUN (neighbor_ttl_security,
 
        gtsm_hops = strtoul(argv[idx_number]->arg, NULL, 10);
 
+       /*
+        * If 'neighbor swpX', then this is for directly connected peers,
+        * we should not accept a ttl-security hops value greater than 1.
+        */
+       if (peer->conf_if && (gtsm_hops > 1)) {
+               vty_out(vty,
+                       "%s is directly connected peer, hops cannot exceed 1\n",
+                       argv[idx_peer]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
        return bgp_vty_return(vty, peer_ttl_security_hops_set(peer, gtsm_hops));
 }
 
 DEFUN (no_neighbor_ttl_security,
        no_neighbor_ttl_security_cmd,
-       "no neighbor <A.B.C.D|X:X::X:X> ttl-security hops (1-254)",
+       "no neighbor <A.B.C.D|X:X::X:X|WORD> ttl-security hops (1-254)",
        NO_STR
        NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR
+       NEIGHBOR_ADDR_STR2
        "BGP ttl-security parameters\n"
        "Specify the maximum number of hops to the BGP peer\n"
        "Number of hops to BGP peer\n")
@@ -6589,17 +6612,21 @@ static void bgp_show_martian_nexthops(struct vty *vty, struct bgp *bgp)
                     vty);
 }
 
-DEFUN(show_bgp_martian_nexthop_db,
-      show_bgp_martian_nexthop_db_cmd,
-      "show bgp martian next-hop",
-      SHOW_STR
-      BGP_STR
+DEFUN(show_bgp_martian_nexthop_db, show_bgp_martian_nexthop_db_cmd,
+      "show bgp [<view|vrf> VIEWVRFNAME] martian next-hop",
+      SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR
       "martian next-hops\n"
       "martian next-hop database\n")
 {
        struct bgp *bgp = NULL;
+       int idx = 0;
+
+       if (argv_find(argv, argc, "view", &idx)
+           || argv_find(argv, argc, "vrf", &idx))
+               bgp = bgp_lookup_by_name(argv[idx + 1]->arg);
+       else
+               bgp = bgp_get_default();
 
-       bgp = bgp_get_default();
        if (!bgp) {
                vty_out(vty, "%% No BGP process is configured\n");
                return CMD_WARNING;
@@ -9847,7 +9874,6 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp,
        }
 
        if (use_json) {
-               bgp_show_bestpath_json(bgp, json);
                vty_out(vty, "%s\n", json_object_to_json_string_ext(
                                             json, JSON_C_TO_STRING_PRETTY));
                json_object_free(json);
@@ -9859,12 +9885,15 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp,
 }
 
 static void bgp_show_all_instances_neighbors_vty(struct vty *vty,
+                                                enum show_type type,
+                                                const char *ip_str,
                                                 u_char use_json)
 {
        struct listnode *node, *nnode;
        struct bgp *bgp;
+       union sockunion su;
        json_object *json = NULL;
-       int is_first = 1;
+       int ret, is_first = 1;
 
        if (use_json)
                vty_out(vty, "{\n");
@@ -9903,8 +9932,19 @@ static void bgp_show_all_instances_neighbors_vty(struct vty *vty,
                                        ? "Default"
                                        : bgp->name);
                }
-               bgp_show_neighbor(vty, bgp, show_all, NULL, NULL, use_json,
-                                 json);
+
+               if (type == show_peer) {
+                       ret = str2sockunion(ip_str, &su);
+                       if (ret < 0)
+                               bgp_show_neighbor(vty, bgp, type, NULL, ip_str,
+                                                 use_json, json);
+                       else
+                               bgp_show_neighbor(vty, bgp, type, &su, NULL,
+                                                 use_json, json);
+               } else {
+                       bgp_show_neighbor(vty, bgp, show_all, NULL, NULL,
+                                         use_json, json);
+               }
        }
 
        if (use_json)
@@ -9922,7 +9962,8 @@ static int bgp_show_neighbor_vty(struct vty *vty, const char *name,
 
        if (name) {
                if (strmatch(name, "all")) {
-                       bgp_show_all_instances_neighbors_vty(vty, use_json);
+                       bgp_show_all_instances_neighbors_vty(vty, type, ip_str,
+                                                            use_json);
                        return CMD_SUCCESS;
                } else {
                        bgp = bgp_lookup_by_name(name);
@@ -11571,7 +11612,7 @@ void bgp_vty_init(void)
        install_element(BGP_NODE, &bgp_listen_range_cmd);
        install_element(BGP_NODE, &no_bgp_listen_range_cmd);
 
-       /* "neighbors auto-shutdown" command */
+       /* "bgp default shutdown" command */
        install_element(BGP_NODE, &bgp_default_shutdown_cmd);
 
        /* "neighbor remote-as" commands. */
index e456f7caed0611ae69420ab8bb135982da091db0..cbb41f0840a526e8e41559f9d08d0aa08510cb53 100644 (file)
@@ -44,9 +44,9 @@ struct bgp;
        "Address Family modifier\n"
 
 extern void bgp_vty_init(void);
-extern const char *afi_safi_print(afi_t, safi_t);
-extern const char *afi_safi_json(afi_t, safi_t);
-extern void bgp_config_write_update_delay(struct vty *, struct bgp *);
+extern const char *afi_safi_print(afi_t afi, safi_t safi);
+extern const char *afi_safi_json(afi_t afi, safi_t safi);
+extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);
 extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
 extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp);
 extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp);
index 8bd594995cbd52342d8651f290dff693efea229c..e0bd74a206992bb38cea75048e43a09f19eb3a2c 100644 (file)
@@ -1767,7 +1767,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
        struct stream *s;
        vni_t vni;
        struct bgp *bgp;
-       struct in_addr vtep_ip;
+       struct in_addr vtep_ip = { INADDR_ANY };
        vrf_id_t tenant_vrf_id = VRF_DEFAULT;
 
        s = zclient->ibuf;
index 7db73043ccb3b47fa0f460d8801facf78de6f8d7..4ff0ef41e0b5cd3898b9b822bec9da25196b0cc2 100644 (file)
@@ -7151,7 +7151,7 @@ int bgp_config_write(struct vty *vty)
 
                /* BGP default autoshutdown neighbors */
                if (bgp->autoshutdown)
-                       vty_out(vty, " bgp default auto-shutdown\n");
+                       vty_out(vty, " bgp default shutdown\n");
 
                /* BGP client-to-client reflection. */
                if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
@@ -7470,30 +7470,33 @@ static void bgp_pthreads_init()
 {
        frr_pthread_init();
 
-       frr_pthread_new("BGP i/o thread", PTHREAD_IO, bgp_io_start,
-                       bgp_io_stop);
-       frr_pthread_new("BGP keepalives thread", PTHREAD_KEEPALIVES,
-                       bgp_keepalives_start, bgp_keepalives_stop);
-
-       /* pre-run initialization */
-       bgp_keepalives_init();
-       bgp_io_init();
+       struct frr_pthread_attr io = {
+               .id = PTHREAD_IO,
+               .start = frr_pthread_attr_default.start,
+               .stop = frr_pthread_attr_default.stop,
+               .name = "BGP I/O thread",
+       };
+       struct frr_pthread_attr ka = {
+               .id = PTHREAD_KEEPALIVES,
+               .start = bgp_keepalives_start,
+               .stop = bgp_keepalives_stop,
+               .name = "BGP Keepalives thread",
+       };
+       frr_pthread_new(&io);
+       frr_pthread_new(&ka);
 }
 
 void bgp_pthreads_run()
 {
-       pthread_attr_t attr;
-       pthread_attr_init(&attr);
-       pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
+       struct frr_pthread *io = frr_pthread_get(PTHREAD_IO);
+       struct frr_pthread *ka = frr_pthread_get(PTHREAD_KEEPALIVES);
 
-       /*
-        * I/O related code assumes the thread is ready for work at all times,
-        * so we wait until it is.
-        */
-       frr_pthread_run(PTHREAD_IO, &attr, NULL);
-       bgp_io_wait_running();
+       frr_pthread_run(io, NULL);
+       frr_pthread_run(ka, NULL);
 
-       frr_pthread_run(PTHREAD_KEEPALIVES, &attr, NULL);
+       /* Wait until threads are ready. */
+       frr_pthread_wait_running(io);
+       frr_pthread_wait_running(ka);
 }
 
 void bgp_pthreads_finish()
index 36100d5429357cb5be1cfd1e9f21b21a579eee92..28d068cc578453ed273c3a8a1cdb0b31b1f2ed1d 100644 (file)
@@ -494,7 +494,7 @@ void rfapiPrintBi(void *stream, struct bgp_info *bi)
        char *p = line;
        int r;
        int has_macaddr = 0;
-       struct ethaddr macaddr;
+       struct ethaddr macaddr = {{0}};
        struct rfapi_l2address_option l2o_buf;
        uint8_t l2hid = 0; /* valid if has_macaddr */
 
index a3b38559e55fa054d5fd14fc6aa20de3ffd32a7e..6bd2d44fe864475545210f4c926982ff11ccef90 100755 (executable)
@@ -7,7 +7,7 @@
 ##
 AC_PREREQ(2.60)
 
-AC_INIT(frr, 3.1-dev, [https://github.com/frrouting/frr/issues])
+AC_INIT(frr, 4.1-dev, [https://github.com/frrouting/frr/issues])
 PACKAGE_URL="https://frrouting.org/"
 AC_SUBST(PACKAGE_URL)
 PACKAGE_FULLNAME="FRRouting"
index 5c3e1363ce66f85c02e1ae7f80f53648cfac9bf6..2e9e84faa6006270c1d4d03bdb57450eea8d68ff 100755 (executable)
@@ -6,23 +6,27 @@
 #    WANT_xxxx   --> Set to 1 for enable, 0 for disable
 # The following are the defaults. They can be overridden by setting a 
 # env variable to a different value
-#
-#   export WANT_LDP=1
-#   export WANT_PIM=1
-#   export WANT_OSPFAPI=1
-#   export WANT_TCP_ZEBRA=0
-#   export WANT_BGP_VNC=0
-#   export WANT_CUMULUS_MODE=0
-#   export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
 # If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
 # Please be aware that 0 is NOT disabled, but treated as unlimited
-#   export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
 # WANT_FRR_USER is used for the username and groupname of the FRR user account
-#   export WANT_FRR_USER=frr
-#   export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
 #
 ####################################
 
@@ -34,22 +38,23 @@ ifeq ($(WANT_SNMP), 1)
   USE_SNMP=--enable-snmp
   $(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
 else
+  USE_SNMP=--disable-snmp
   $(warning "DEBIAN: SNMP disabled, see README.Debian")
 endif
 
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
   USE_LDP=--enable-ldpd
 else
   USE_LDP=--disable-ldpd
 endif
 
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
   USE_PIM=--enable-pimd
 else
   USE_PIM=--disable-pimd
 endif
 
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
   USE_OSPFAPI=--enable-ospfapi=yes
 else
   USE_OSPFAPI=--enable-ospfapi=no
@@ -57,39 +62,27 @@ endif
 
 ifeq ($(WANT_TCP_ZEBRA),1)
   USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+  USE_TCP_ZEBRA=--disable-tcp-zebra
 endif
 
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
   USE_BGP_VNC=--enable-bgp-vnc=yes
 else
   USE_BGP_VNC=--enable-bgp-vnc=no
 endif
 
-ifndef WANT_FRR_USER
-  USE_FRR_USER=--enable-user=frr
-  USE_FRR_GROUP=--enable-group=frr
-else
-  USE_FRR_USER=$(WANT_FRR_USER)
-  USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
-
-ifndef WANT_FRR_VTY_GROUP
-  USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
-  USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
 
-ifneq ($(WANT_MULTIPATH), 0)
-  ifdef MULTIPATH
-    USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
-  else
-    USE_MULTIPATH=--enable-multipath=256
-  endif
+ifeq ($(WANT_MULTIPATH), 1)
+  USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
 else
   USE_MULTIPATH=--disable-multipath
 endif
 
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
   USE_CUMULUS=--enable-cumulus=yes
 else
   USE_CUMULUS=--enable-cumulus=no
@@ -171,9 +164,12 @@ override_dh_auto_install:
        perl -pi -e 's#^!log file #!log file /var/log/frr/#' debian/tmp/usr/share/doc/frr/examples/*sample*
 
        # installing the Frr specific SNMP MIB
+ifeq ($(WANT_SNMP), 1)
        install -D -m 644 ./zebra/GNOME-PRODUCT-ZEBRA-MIB debian/tmp/usr/share/snmp/mibs/GNOME-PRODUCT-ZEBRA-MIB
+else
+       mkdir -p debian/tmp/usr/share/snmp/mibs
+endif
 
        # cleaning .la files
        sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/*.la
        sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/frr/modules/*.la
-
index b1f539def69b611f749810032a718035cd342cfb..4f82d772c900810c28eae44226748edbe80cb56a 100755 (executable)
@@ -6,23 +6,27 @@
 #    WANT_xxxx   --> Set to 1 for enable, 0 for disable
 # The following are the defaults. They can be overridden by setting a 
 # env variable to a different value
-#
-#   export WANT_LDP=1
-#   export WANT_PIM=1
-#   export WANT_OSPFAPI=1
-#   export WANT_TCP_ZEBRA=0
-#   export WANT_BGP_VNC=0
-#   export WANT_CUMULUS_MODE=0
-#   export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
 # If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
 # Please be aware that 0 is NOT disabled, but treated as unlimited
-#   export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
 # WANT_FRR_USER is used for the username and groupname of the FRR user account
-#   export WANT_FRR_USER=frr
-#   export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
 #
 ####################################
 
@@ -34,22 +38,23 @@ ifeq ($(WANT_SNMP), 1)
   USE_SNMP=--enable-snmp
   $(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
 else
+  USE_SNMP=--disable-snmp
   $(warning "DEBIAN: SNMP disabled, see README.Debian")
 endif
 
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
   USE_LDP=--enable-ldpd
 else
   USE_LDP=--disable-ldpd
 endif
 
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
   USE_PIM=--enable-pimd
 else
   USE_PIM=--disable-pimd
 endif
 
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
   USE_OSPFAPI=--enable-ospfapi=yes
 else
   USE_OSPFAPI=--enable-ospfapi=no
@@ -57,39 +62,27 @@ endif
 
 ifeq ($(WANT_TCP_ZEBRA),1)
   USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+  USE_TCP_ZEBRA=--disable-tcp-zebra
 endif
 
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
   USE_BGP_VNC=--enable-bgp-vnc=yes
 else
   USE_BGP_VNC=--enable-bgp-vnc=no
 endif
 
-ifndef WANT_FRR_USER
-  USE_FRR_USER=--enable-user=frr
-  USE_FRR_GROUP=--enable-group=frr
-else
-  USE_FRR_USER=$(WANT_FRR_USER)
-  USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
 
-ifndef WANT_FRR_VTY_GROUP
-  USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
-  USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
-
-ifneq ($(WANT_MULTIPATH), 0)
-  ifdef MULTIPATH
-    USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
-  else
-    USE_MULTIPATH=--enable-multipath=256
-  endif
+ifeq ($(WANT_MULTIPATH), 1)
+  USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
 else
   USE_MULTIPATH=--disable-multipath
 endif
 
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
   USE_CUMULUS=--enable-cumulus=yes
 else
   USE_CUMULUS=--enable-cumulus=no
@@ -171,7 +164,11 @@ override_dh_auto_install:
        perl -pi -e 's#^!log file #!log file /var/log/frr/#' debian/tmp/usr/share/doc/frr/examples/*sample*
 
        # installing the Frr specific SNMP MIB
+ifeq ($(WANT_SNMP), 1)
        install -D -m 644 ./zebra/GNOME-PRODUCT-ZEBRA-MIB debian/tmp/usr/share/snmp/mibs/GNOME-PRODUCT-ZEBRA-MIB
+else
+       mkdir -p debian/tmp/usr/share/snmp/mibs/
+endif
 
        # cleaning .la files
        sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/*.la
index a6a9077da181cafa2568b0c82beab6c725bb6650..574f448dffe997e434c8f5e097a59bb64a86dc45 100755 (executable)
@@ -6,23 +6,27 @@
 #    WANT_xxxx   --> Set to 1 for enable, 0 for disable
 # The following are the defaults. They can be overridden by setting a 
 # env variable to a different value
-#
-#   export WANT_LDP=1
-#   export WANT_PIM=1
-#   export WANT_OSPFAPI=1
-#   export WANT_TCP_ZEBRA=0
-#   export WANT_BGP_VNC=0
-#   export WANT_CUMULUS_MODE=0
-#   export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
 # If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
 # Please be aware that 0 is NOT disabled, but treated as unlimited
-#   export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
 # WANT_FRR_USER is used for the username and groupname of the FRR user account
-#   export WANT_FRR_USER=frr
-#   export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
 #
 ####################################
 
@@ -34,22 +38,23 @@ ifeq ($(WANT_SNMP), 1)
   USE_SNMP=--enable-snmp
   $(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
 else
+  USE_SNMP=--disable-snmp
   $(warning "DEBIAN: SNMP disabled, see README.Debian")
 endif
 
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
   USE_LDP=--enable-ldpd
 else
   USE_LDP=--disable-ldpd
 endif
 
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
   USE_PIM=--enable-pimd
 else
   USE_PIM=--disable-pimd
 endif
 
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
   USE_OSPFAPI=--enable-ospfapi=yes
 else
   USE_OSPFAPI=--enable-ospfapi=no
@@ -57,39 +62,27 @@ endif
 
 ifeq ($(WANT_TCP_ZEBRA),1)
   USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+  USE_TCP_ZEBRA=--disable-tcp-zebra
 endif
 
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
   USE_BGP_VNC=--enable-bgp-vnc=yes
 else
   USE_BGP_VNC=--enable-bgp-vnc=no
 endif
 
-ifndef WANT_FRR_USER
-  USE_FRR_USER=--enable-user=frr
-  USE_FRR_GROUP=--enable-group=frr
-else
-  USE_FRR_USER=$(WANT_FRR_USER)
-  USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
-
-ifndef WANT_FRR_VTY_GROUP
-  USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
-  USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
 
-ifneq ($(WANT_MULTIPATH), 0)
-  ifdef MULTIPATH
-    USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
-  else
-    USE_MULTIPATH=--enable-multipath=256
-  endif
+ifeq ($(WANT_MULTIPATH), 1)
+  USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
 else
   USE_MULTIPATH=--disable-multipath
 endif
 
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
   USE_CUMULUS=--enable-cumulus=yes
 else
   USE_CUMULUS=--enable-cumulus=no
diff --git a/doc/OSPF-SR.rst b/doc/OSPF-SR.rst
new file mode 100644 (file)
index 0000000..0ee1a12
--- /dev/null
@@ -0,0 +1,269 @@
+OSPF Segment Routing
+====================
+
+This is an EXPERIMENTAL support of draft
+`draft-ietf-ospf-segment-routing-extensions-24`.
+DON'T use it for production network.
+
+Implementation details
+----------------------
+
+Concepts
+~~~~~~~~
+
+Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various
+information:
+
+* **Router Information:** flood the Segment Routing capabilities of the node.
+  This include the supported algorithms, the Segment Routing Global Block
+  (SRGB) and the Maximum Stack Depth (MSD).
+* **Extended Link:** flood the Adjaceny and Lan Adjacency Segment Identifier
+* **Extended Prefix:** flood the Prefix Segment Identifier
+
+The implementation follow previous TE and Router Information codes. It used the
+OPAQUE LSA functions defined in ospf_opaque.[c,h] as well as the OSPF API. This
+latter is mandatory for the implementation as it provides the Callback to
+Segment Routing functions (see below) when an Extended Link / Prefix or Router
+Information LSA s are received.
+
+Overview
+~~~~~~~~
+
+Following files where modified or added:
+
+* ospd_ri.[c,h] have been modified to add the new TLVs for Segment Routing.
+* ospf_ext.[c,h] implement RFC7684 as base support of Extended Link and Prefix
+  Opaque LSA.
+* ospf_sr.[c,h] implement the earth of Segment Routing. It adds a new Segment
+  Routing database to manage Segment Identifiers per Link and Prefix and
+  Segment Routing enable node, Callback functions to process incoming LSA and
+  install MPLS FIB entry through Zebra.
+
+The figure below shows the relation between the various files:
+
+* ospf_sr.c centralized all the Segment Routing processing. It receives Opaque
+  LSA Router Information (4.0.0.0) from ospf_ri.c and Extended Prefix
+  (7.0.0.X) Link (8.0.0.X) from ospf_ext.c. Once received, it parse TLVs and
+  SubTLVs and store information in SRDB (which is defined in ospf_sr.h). For
+  each received LSA, NHLFE is computed and send to Zebra to add/remove new
+  MPLS labels entries and FEC. New CLI configurations are also centralized in
+  ospf_sr.c. This CLI will trigger the flooding of new LSA Router Information
+  (4.0.0.0), Extended Prefix (7.0.0.X) and Link (8.0.0.X) by ospf_ri.c,
+  respectively ospf_ext.c.
+* ospf_ri.c send back to ospf_sr.c received Router Information LSA and update
+  Self Router Information LSA with paramters provided by ospf_sr.c i.e. SRGB
+  and MSD. It use ospf_opaque.c functions to send/received these Opaque LSAs.
+* ospf_ext.c send back to ospf_sr.c received Extended Prefix and Link Opaque
+  LSA and send self Extended Prefix and Link Opaque LSA through ospf_opaque.c
+  functions.
+
+::
+
+                    +-----------+     +-------+
+                    |           |     |       |
+                    | ospf_sr.c +-----+  SRDB |
+        +-----------+           +--+  |       |
+        |           +-^-------^-+  |  +-------+
+        |             |   |   |    |
+        |             |   |   |    |
+        |             |   |   |    +--------+
+        |             |   |   |             |
+    +---v----------+  |   |   |       +-----v-------+
+    |              |  |   |   |       |             |
+    | ospf_ri.c    +--+   |   +-------+ ospf_ext.c  |
+    | LSA 4.0.0.0  |      |           | LSA 7.0.0.X |
+    |              |      |           | LSA 8.0.0.X |
+    +---^----------+      |           |             |
+        |                 |           +-----^-------+
+        |                 |                 |
+        |                 |                 |
+        |        +--------v------------+    |
+        |        |                     |    |
+        |        | ZEBRA: Labels + FEC |    |
+        |        |                     |    |
+        |        +---------------------+    |
+        |                                   |
+        |                                   |
+        |         +---------------+         |
+        |         |               |         |
+        +---------> ospf_opaque.c <---------+
+                  |               |
+                  +---------------+
+
+      Figure 1: Overview of Segment Routing interaction
+
+Module interactions
+~~~~~~~~~~~~~~~~~~~
+
+To process incoming LSA, the code is based on the capability to call `hook()`
+functions when LSA are inserted or delete to / from the LSDB and the
+possibility to register particular treatment for Opaque LSA. The first point
+is provided by the OSPF API feature and the second by the Opaque implementation
+itself. Indeed, it is possible to register callback function for a given Opaque
+LSA ID (see `ospf_register_opaque_functab()` function defined in
+`ospf_opaque.c`). Each time a new LSA is added to the LSDB, the
+`new_lsa_hook()` function previously register for this LSA type is called. For
+Opaque LSA it is the `ospf_opaque_lsa_install_hook()`.  For deletion, it is
+`ospf_opaque_lsa_delete_hook()`.
+
+Note that incoming LSA which is already present in the LSDB will be inserted
+after the old instance of this LSA remove from the LSDB. Thus, after the first
+time, each incoming LSA will trigger a `delete` following by an `install`. This
+is not very helpfull to handle real LSA deletion. In fact, LSA deletion is done
+by Flushing LSA i.e. flood LSA after seting its age to MAX_AGE. Then, a garbage
+function has the role to remove all LSA with `age == MAX_AGE` in the LSDB. So,
+to handle LSA Flush, the best is to look to the LSA age to determine if it is
+an installation or a future deletion i.e. the flushed LSA is first store in the
+LSDB with MAX_AGE waiting for the garbage collector function.
+
+Router Information LSAs
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To activate Segment Routing, new CLI command `segment-routing on` has been
+introduced. When this command is activated, function
+`ospf_router_info_update_sr()` is called to indicate to Router Information
+process that Segment Routing TLVs must be flood. Same function is called to
+modify the Segment Routing Global Block (SRGB) and Maximum Stack Depth (MSD)
+TLV. Only Shortest Path First (SPF) Algorithm is supported, so no possiblity
+to modify this TLV is offer by the code.
+
+When Opaque LSA Tyep 4 i.e. Router Information are stored in LSDB, function
+`ospf_opaque_lsa_install_hook()` will call the previously registered function
+`ospf_router_info_lsa_update()`. In turn, the function will simply trigger
+`ospf_sr_ri_lsa_update()` or `ospf_sr_ri_lsa_delete` in function of the LSA
+age. Before, it verifies that the LSA Opaque Type is 4 (Router Information).
+Self Opaque LSA are not send back to the Segment Routing functions as
+information are already stored.
+
+Extended Link Prefix LSAs
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like for Router Information, Segment Routing is activate at the Extended
+Link/Prefix level with new `segment-routing on` command. This trigger
+automtically the flooding of Extended Link LSA for all ospf interface where
+adjacency is full. For Extended Prefix LSA, the new CLI command
+`segment-routing prefix ...` will trigger the flooding of Prefix SID
+TLV/SubTLVs.
+
+When Opaque LSA Type 7 i.e. Extended Prefix and Type 8 i.e. Extended Link are
+store in the LSDB, `ospf_ext_pref_update_lsa()` respectively
+`ospf_ext_link_update_lsa()` are called like for Router Information LSA. In
+turn, they respectively trigger `ospf_sr_ext_prefix_lsa_update()` /
+`ospf_sr_ext_link_lsa_update()` or `ospf_sr_ext_prefix_lsa_delete()` /
+`ospf_sr_ext_link_lsa_delete()` if the LSA age is equal to MAX_AGE.
+
+Zebra
+^^^^^
+
+When a new MPLS entry or new Forwarding Equivalent Class (FEC) must be added or
+deleted in the data plane, `add_sid_nhlfe()` respectively `del_sid_nhlfe()` are
+called. Once check the validity of labels, they are send to ZEBRA layer through
+`ZEBRA_MPLS_LABELS_ADD` command, respectively `ZEBRA_MPLS_LABELS_DELETE`
+command for deletion. This is completed by a new labelled route through
+`ZEBRA_ROUTE_ADD` command, respectively `ZEBRA_ROUTE_DELETE` command.
+
+Configuration
+-------------
+
+Linux Kernel
+~~~~~~~~~~~~
+
+In order to use OSPF Segment Routing, you must setup MPLS data plane. Up to
+know, only Linux Kernel version >= 4.5 is supported.
+
+First, the MPLS modules aren't loaded by default, so you'll need to load them
+yourself:
+
+::
+
+       modprobe mpls_router
+       modprobe mpls_gso
+       modprobe mpls_iptunnel
+
+Then, you must activate MPLS on the interface you would used:
+
+::
+
+       sysctl -w net.mpls.conf.enp0s9.input=1
+       sysctl -w net.mpls.conf.lo.input=1
+       sysctl -w net.mpls.platform_labels=1048575
+
+The last line fix the maximum MPLS label value.
+
+Once OSPFd start with Segment Routing, you could check that MPLS routes are
+enable with:
+
+::
+
+       ip -M route
+       ip route
+
+The first command show the MPLS LFIB table while the second show the FIB
+table which contains route with MPLS label encapsulation.
+
+If you disable Penultimate Hop Popping with the `no-php-flag` (see below), you
+MUST check that RP filter is not enable for the interface you intend to use,
+especially the `lo` one. For that purpose, disable RP filtering with:
+
+::
+
+       systcl -w net.ipv4.conf.all.rp_filter=0
+       sysctl -w net.ipv4.conf.lo.rp_filter=0
+
+OSPFd
+~~~~~
+
+Here it is a simple example of configuration to enable Segment Routing. Note
+that `opaque capability` and `router information` must be set to activate
+Opaque LSA prior to Segment
+Routing.
+
+::
+
+       router ospf
+        ospf router-id 192.168.1.11
+        capability opaque
+         mpls-te on
+         mpls-te router-address 192.168.1.11
+        router-info area 0.0.0.0
+        segment-routing on
+        segment-routing global-block 10000 19999
+        segment-routing node-msd 8
+        segment-routing prefix 192.168.1.11/32 index 1100
+
+The first segment-routing statement enable it. The Second one set the SRGB,
+third line the MSD and finally, set the Prefix SID index for a given prefix.
+Note that only prefix of Loopback interface could be configured with a Prefix
+SID. It is possible to add `no-php-flag` at the end of the prefix command to
+disbale Penultimate Hop Popping. This advertises peers that they MUST NOT pop
+the MPLS label prior to sending the packet.
+
+Known limitations
+-----------------
+
+* Runs only within default VRF
+* Only single Area is supported. ABR is not yet supported
+* Only SPF algorithm is supported
+* Extended Prefix Range is not supported
+* MPLS table are not flush at startup. Thus, restarting zebra process is
+  mandatory to remove old MPLS entries in the data plane after a crash of
+  ospfd daemon
+* Due to a bug in OSPF Opaque, LSA are not flood when enable Segment Routing
+  through CLI once OSPFd started. You must configure Segment Routing within
+  configuration file before launching OSPFd
+* With NO Penultimate Hop Popping, it is not possible to express a Segment
+  Path with an Adjacency SID due to the impossibility for the Linux Kernel to
+  perform double POP instruction.
+
+Credits
+-------
+
+* Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+* Author: Olivier Dugeon <olivier.dugeon@orange.com>
+* Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+
+This work has been performed in the framework of the H2020-ICT-2014
+project 5GEx (Grant Agreement no. 671636), which is partially funded
+by the European Commission.
+
+
index d62c3a7516128a4610d109b5327c53050e53751e..83483f569739af7676816663c10dfc4d59ed420c 100644 (file)
@@ -823,22 +823,15 @@ Network Layer protocols. BGP supports multiple Address Family
 Identifier (AFI), namely IPv4 and IPv6. Support is also provided for
 multiple sets of per-AFI information via Subsequent Address Family
 Identifiers (SAFI).  In addition to unicast information, VPN information
-@cite{RFC4364} and @cite{RFC4659}, and Encapsulation information
+@cite{RFC4364} and @cite{RFC4659}, and Encapsulation attribute
 @cite{RFC5512} is supported.
 
-@deffn {Command} {show ip bgp vpnv4 all} {}
-@deffnx {Command} {show ipv6 bgp vpn all} {}
+@deffn {Command} {show ip bgp ipv4 vpn} {}
+@deffnx {Command} {show ipv6 bgp ipv6 vpn} {}
 Print active IPV4 or IPV6 routes advertised via the VPN SAFI.
 @end deffn
 
-@deffn {Command} {show ip bgp encap all} {}
-@deffnx {Command} {show ipv6 bgp encap all} {}
-Print active IPV4 or IPV6 routes advertised via the Encapsulation SAFI.
-@end deffn
-
-@deffn {Command} {show bgp ipv4 encap summary} {}
-@deffnx {Command} {show bgp ipv4 vpn summary} {}
-@deffnx {Command} {show bgp ipv6 encap summary} {}
+@deffn {Command} {show bgp ipv4 vpn summary} {}
 @deffnx {Command} {show bgp ipv6 vpn summary} {}
 Print a summary of neighbor connections for the specified AFI/SAFI combination.
 @end deffn
index 26a7637048c14d090e21104a1988cb9cced49693..33341d2be6de6a8e3a010d578bc7657548ec7297 100644 (file)
@@ -22,6 +22,7 @@ networks.
 * Opaque LSA::
 * OSPF Traffic Engineering::
 * Router Information::
+* Segment Routing::
 * Debugging OSPF::              
 * OSPF Configuration Examples::
 @end menu
@@ -724,6 +725,46 @@ Show Router Capabilities flag.
 Show Router Capabilities PCE parameters.
 @end deffn
 
+@node Segment Routing
+@section Segment Routing
+
+This is an EXPERIMENTAL support of Segment Routing as per draft
+ draft-ietf-ospf-segment-routing-extensions-24.txt for MPLS dataplane.
+
+@deffn {OSPF Command} {segment-routing on} {}
+@deffnx {OSPF Command} {no segment-routing} {}
+Enable Segment Routing. Even if this also activate routing information support,
+it is preferable to also activate routing information, and set accordingly the
+Area or AS flooding.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing global-block (0-1048575) (0-1048575)} {}
+@deffnx {OSPF Command} {no segment-routing global-block} {}
+Fix the Segment Routing Global Block i.e. the label range used by MPLS to store
+label in the MPLS FIB.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing node-msd (1-16)} {}
+@deffnx {OSPF Command} {no segment-routing node-msd} {}
+Fix the Maximum Stack Depth supported by the router. The value depend of the
+MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535)} {}
+@deffnx {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535) no-php-flag} {}
+@deffnx {OSPF Command} {no segment-routing prefix A.B.C.D/M} {}
+Set the Segment Rounting index for the specifyed prefix. Note
+that, only prefix with /32 corresponding to a loopback interface are
+currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that
+allows SR node to request to its neighbor to not pop the label.
+@end deffn
+
+@deffn {Command} {show ip ospf database segment-routing} {}
+@deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database segment-routing self-originate} {}
+Show Segment Routing Data Base, all SR nodes, specific advertized router or self router.
+@end deffn
+
 @node Debugging OSPF
 @section Debugging OSPF
 
index c44519a9f3af6dba08c479255768eac604db7d0a..6193d3c906f6275db27a3c2c0a9ea3b653768f22 100644 (file)
@@ -22,8 +22,8 @@ BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VN
 information between NVAs. BGP based IP VPN support is defined in
 @cite{RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs)}, and
 @cite{RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for
-IPv6 VPN }.  Both the Encapsulation Subsequent Address Family Identifier
-(SAFI) and the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP
+IPv6 VPN }.  Encapsulation information is provided via
+the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP
 Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP
 Tunnel Encapsulation Attribute}, are supported.
 
@@ -83,12 +83,10 @@ operating within a VN.
 @node General VNC Configuration
 @subsection General VNC Configuration
 
-@deffn {VNC} {vnc advertise-un-method encap-safi|encap-attr} {}
-Advertise NVE underlay-network IP addresses using the encapsulation SAFI
-(@code{encap-safi}) or the UN address sub-TLV of the Tunnel Encapsulation attribute
-(@code{encap-attr}). When @code{encap-safi} is used, neighbors under 
-@code{address-family encap} and/or @code{address-family encapv6} must be
-configured.  The default is @code{encap-attr}. 
+@deffn {VNC} {vnc advertise-un-method encap-attr} {}
+Advertise NVE underlay-network IP addresses using
+the UN address sub-TLV of the Tunnel Encapsulation attribute
+(@code{encap-attr}). The default is @code{encap-attr}. 
 @end deffn
 
 @node RFP Related Configuration
@@ -356,8 +354,7 @@ by receiving NVAs.
 The second form, @code{rt import} specifies an @var{import rt-list},
 which is a filter for incoming routes.
 In order to be made available to NVEs in the group,
-incoming BGP VPN and @w{ENCAP} @w{SAFI} (when @code{vnc
-advertise-un-method encap-safi} is set) routes must have
+incoming BGP VPN @w{SAFI} routes must have
 RT lists that have at least one route target in common with the
 group's @var{import rt-list}.
 
@@ -1010,7 +1007,7 @@ router bgp 64512
     neighbor 192.168.1.101  remote-as 64512
     neighbor 192.168.1.102  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.101 activate
         neighbor 192.168.1.102 activate
     exit-address-family
@@ -1043,7 +1040,7 @@ router bgp 64512
     neighbor 192.168.1.100  remote-as 64512
     neighbor 192.168.1.102  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
         neighbor 192.168.1.102 activate
     exit-address-family
@@ -1066,7 +1063,7 @@ router bgp 64512
     neighbor 192.168.1.101  remote-as 64512
     neighbor 192.168.1.102  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
         neighbor 192.168.1.101 activate
     exit-address-family
@@ -1131,7 +1128,7 @@ router bgp 64512
   neighbor 172.16.2.2 route-reflector-client
  exit-address-family
  !
- address-family vpnv4 unicast
+ address-family ipv4 vpn
    neighbor 192.168.1.102 activate
    neighbor 192.168.1.103 activate
    neighbor 192.168.1.104 activate
@@ -1161,7 +1158,7 @@ router bgp 64512
   no neighbor 192.168.1.103 activate
  exit-address-family
  !
- address-family vpnv4 unicast
+ address-family ipv4 vpn
    neighbor 192.168.1.101 activate
    neighbor 192.168.1.102 activate
    neighbor 192.168.1.103 activate
@@ -1250,7 +1247,7 @@ router bgp 64512
         neighbor 192.168.1.102 route-reflector-client
     exit-address-family
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.101 activate
         neighbor 192.168.1.102 activate
 
@@ -1269,7 +1266,7 @@ router bgp 64512
 
     neighbor 192.168.1.100  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
     exit-address-family
 
@@ -1290,7 +1287,7 @@ router bgp 64512
 
     neighbor 192.168.1.100  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
     exit-address-family
 
@@ -1381,7 +1378,7 @@ router bgp 64512
 
     neighbor 192.168.1.100  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
     exit-address-family
 
@@ -1402,7 +1399,7 @@ router bgp 64512
 
     neighbor 192.168.1.100  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
     exit-address-family
 
@@ -1450,7 +1447,7 @@ router bgp 64512
     neighbor 192.168.1.102 description iBGP-client-192-168-1-102
     neighbor 192.168.1.102 route-reflector-client
 
-    address-family vpnv4
+    address-family ipv4 vpn
        neighbor 192.168.1.101 activate
        neighbor 192.168.1.102 activate
        neighbor 192.168.1.104 activate
@@ -1470,7 +1467,7 @@ router bgp 64512
     neighbor 192.168.1.100  remote-as 64512
     neighbor 192.168.1.104  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
         neighbor 192.168.1.104 activate
     exit-address-family
@@ -1493,7 +1490,7 @@ router bgp 64512
     neighbor 192.168.1.100  remote-as 64512
     neighbor 192.168.1.104  remote-as 64512
 
-    address-family vpnv4
+    address-family ipv4 vpn
         neighbor 192.168.1.100 activate
         neighbor 192.168.1.104 activate
     exit-address-family
index 0b7dc86ad6c88f58294d58ec3a0562135d965ddc..95e02f86919dcadd181b476602c592b2d7bdb3c2 100644 (file)
@@ -554,7 +554,7 @@ void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream)
 
 void isis_circuit_prepare(struct isis_circuit *circuit)
 {
-#ifdef GNU_LINUX
+#if ISIS_METHOD != ISIS_METHOD_DLPI
        thread_add_read(master, isis_receive, circuit, circuit->fd,
                        &circuit->t_read);
 #else
index 614f46c78bb6795dcd63be2b9e2f7963b6d7c284..e1e9ccee48fd30629db20b4a15d09de721bf63ba 100644 (file)
@@ -286,7 +286,7 @@ static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer, bool keep)
                (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE;
        struct isis_lsp_hdr *hdr = &lsp->hdr;
        struct stream *stream = lsp->pdu;
-       size_t orig_getp, orig_endp;
+       size_t orig_getp = 0, orig_endp = 0;
 
        if (keep) {
                orig_getp = stream_get_getp(lsp->pdu);
index b7389947b7c0140b8631d3d6640e659be1a69af1..55b0a362f63b17c6ba95364cb76278f9e4cc144c 100644 (file)
@@ -1312,7 +1312,7 @@ static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
        bool sane = true;
        for (uint8_t i = 0; i < tlv_len; i++) {
                if ((unsigned char)tlvs->hostname[i] > 127
-                   || !isprint(tlvs->hostname[i])) {
+                   || !isprint((int)tlvs->hostname[i])) {
                        sane = false;
                        tlvs->hostname[i] = '?';
                }
index 83c91c4c6078452fed3659b3b4b2c686c36da116..d17f2c3d48cb99bce0a48420cad0ecb4b9c4db43 100644 (file)
@@ -96,7 +96,6 @@ const char *node_names[] = {
        "ldp l2vpn",                // LDP_L2VPN_NODE,
        "ldp",                      // LDP_PSEUDOWIRE_NODE,
        "isis",                     // ISIS_NODE,
-       "pim",                      // PIM_NODE,
        "masc",                     // MASC_NODE,
        "irdp",                     // IRDP_NODE,
        "static ip",                // IP_NODE,
@@ -1308,7 +1307,6 @@ void cmd_exit(struct vty *vty)
        case KEYCHAIN_NODE:
        case MASC_NODE:
        case RMAP_NODE:
-       case PIM_NODE:
        case VTY_NODE:
                vty->node = CONFIG_NODE;
                break;
@@ -1414,7 +1412,6 @@ DEFUN (config_end,
        case KEYCHAIN_NODE:
        case KEYCHAIN_KEY_NODE:
        case MASC_NODE:
-       case PIM_NODE:
        case VTY_NODE:
        case LINK_PARAMS_NODE:
                vty_config_unlock(vty);
@@ -1865,7 +1862,7 @@ DEFUN (config_password,
                return CMD_SUCCESS;
        }
 
-       if (!isalnum(argv[idx_8]->arg[0])) {
+       if (!isalnum((int)argv[idx_8]->arg[0])) {
                vty_out(vty,
                        "Please specify string starting with alphanumeric\n");
                return CMD_WARNING_CONFIG_FAILED;
@@ -1917,7 +1914,7 @@ DEFUN (config_enable_password,
                }
        }
 
-       if (!isalnum(argv[idx_8]->arg[0])) {
+       if (!isalnum((int)argv[idx_8]->arg[0])) {
                vty_out(vty,
                        "Please specify string starting with alphanumeric\n");
                return CMD_WARNING_CONFIG_FAILED;
index fa8323bf2ddb5a49d8e0d8847589bd41c385849f..e1edc1ef321901001acf80a271600692b5e19017 100644 (file)
@@ -119,7 +119,6 @@ enum node_type {
        LDP_L2VPN_NODE,         /* LDP L2VPN node */
        LDP_PSEUDOWIRE_NODE,    /* LDP Pseudowire node */
        ISIS_NODE,              /* ISIS protocol mode */
-       PIM_NODE,               /* PIM protocol mode */
        MASC_NODE,              /* MASC for multicast.  */
        IRDP_NODE,              /* ICMP Router Discovery Protocol mode. */
        IP_NODE,                /* Static ip route node. */
@@ -358,6 +357,7 @@ struct cmd_node {
 #define OSPF_RI_STR "OSPF Router Information specific commands\n"
 #define PCE_STR "PCE Router Information specific commands\n"
 #define MPLS_STR "MPLS information\n"
+#define SR_STR "Segment-Routing specific commands\n"
 #define WATCHFRR_STR "watchfrr information\n"
 #define ZEBRA_STR "Zebra information\n"
 
index fce11a70cc9053deed10e5f03611814ea7d33953..f00b126536e98e4774b7374e9c6a34501821c056 100644 (file)
@@ -97,7 +97,7 @@ void cmd_token_varname_set(struct cmd_token *token, const char *varname)
                        token->varname[i] = '_';
                        break;
                default:
-                       token->varname[i] = tolower(varname[i]);
+                       token->varname[i] = tolower((int)varname[i]);
                }
        token->varname[len] = '\0';
 }
index 436f3a241d8ae85455e3f5c4df535f4504b6c1c7..530900659b6c9edd2b95a95cc15044d29f3b8ad0 100644 (file)
@@ -23,8 +23,9 @@
  */
 
 %{
-/* ignore harmless bug in old versions of flex */
+/* ignore harmless bugs in old versions of flex */
 #pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
 
 #include "command_parse.h"
 
index 024445be638ca20c57e11a925664b63418e63f0f..87775a0e7550f1532d93f2cad14b8e7e56791fb9 100644 (file)
@@ -34,8 +34,9 @@
  * code documentation in it.
  */
 
-/* ignore harmless bug in old versions of flex */
+/* ignore harmless bugs in old versions of flex */
 #pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wunused-value"
 
 #include "config.h"
 #include <Python.h>
index de522e5ef9511b8c6e47b88cf624487ecbea711f..72b47ae5c32ad76a1c6bbabc17abc988d98820db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Utilities and interfaces for managing POSIX threads
+ * Utilities and interfaces for managing POSIX threads within FRR.
  * Copyright (C) 2017  Cumulus Networks
  *
  * This program is free software; you can redistribute it and/or modify
 #include "memory.h"
 #include "hash.h"
 
-DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
+DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread");
 DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives");
 
+/* id for next created pthread */
 static unsigned int next_id = 0;
 
-/* Hash table of all frr_pthreads along with synchronization primitive(s) and
- * hash table callbacks.
- * ------------------------------------------------------------------------ */
-static struct hash *pthread_table;
-static pthread_mutex_t pthread_table_mtx = PTHREAD_MUTEX_INITIALIZER;
+/* default frr_pthread start/stop routine prototypes */
+static void *fpt_run(void *arg);
+static int fpt_halt(struct frr_pthread *fpt, void **res);
 
-/* pthread_table->hash_cmp */
-static int pthread_table_hash_cmp(const void *value1, const void *value2)
+/* default frr_pthread attributes */
+struct frr_pthread_attr frr_pthread_attr_default = {
+       .id = 0,
+       .start = fpt_run,
+       .stop = fpt_halt,
+       .name = "Anonymous",
+};
+
+/* hash table to keep track of all frr_pthreads */
+static struct hash *frr_pthread_hash;
+static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* frr_pthread_hash->hash_cmp */
+static int frr_pthread_hash_cmp(const void *value1, const void *value2)
 {
        const struct frr_pthread *tq1 = value1;
        const struct frr_pthread *tq2 = value2;
 
-       return (tq1->id == tq2->id);
+       return (tq1->attr.id == tq2->attr.id);
 }
 
-/* pthread_table->hash_key */
-static unsigned int pthread_table_hash_key(void *value)
+/* frr_pthread_hash->hash_key */
+static unsigned int frr_pthread_hash_key(void *value)
 {
-       return ((struct frr_pthread *)value)->id;
+       return ((struct frr_pthread *)value)->attr.id;
 }
+
 /* ------------------------------------------------------------------------ */
 
 void frr_pthread_init()
 {
-       pthread_mutex_lock(&pthread_table_mtx);
+       pthread_mutex_lock(&frr_pthread_hash_mtx);
        {
-               pthread_table = hash_create(pthread_table_hash_key,
-                                           pthread_table_hash_cmp, NULL);
+               frr_pthread_hash = hash_create(frr_pthread_hash_key,
+                                              frr_pthread_hash_cmp, NULL);
        }
-       pthread_mutex_unlock(&pthread_table_mtx);
+       pthread_mutex_unlock(&frr_pthread_hash_mtx);
 }
 
 void frr_pthread_finish()
 {
-       pthread_mutex_lock(&pthread_table_mtx);
+       pthread_mutex_lock(&frr_pthread_hash_mtx);
        {
-               hash_clean(pthread_table,
+               hash_clean(frr_pthread_hash,
                           (void (*)(void *))frr_pthread_destroy);
-               hash_free(pthread_table);
+               hash_free(frr_pthread_hash);
        }
-       pthread_mutex_unlock(&pthread_table_mtx);
+       pthread_mutex_unlock(&frr_pthread_hash_mtx);
 }
 
-struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
-                                   void *(*start_routine)(void *),
-                                   int (*stop_routine)(void **,
-                                                       struct frr_pthread *))
+struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr)
 {
        static struct frr_pthread holder = {0};
        struct frr_pthread *fpt = NULL;
 
-       pthread_mutex_lock(&pthread_table_mtx);
+       attr = attr ? attr : &frr_pthread_attr_default;
+
+       pthread_mutex_lock(&frr_pthread_hash_mtx);
        {
-               holder.id = id;
-
-               if (!hash_lookup(pthread_table, &holder)) {
-                       struct frr_pthread *fpt = XCALLOC(
-                               MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
-                       fpt->id = id;
-                       fpt->master = thread_master_create(name);
-                       fpt->start_routine = start_routine;
-                       fpt->stop_routine = stop_routine;
-                       fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
-
-                       hash_get(pthread_table, fpt, hash_alloc_intern);
+               holder.attr.id = attr->id;
+
+               if (!hash_lookup(frr_pthread_hash, &holder)) {
+                       fpt = XCALLOC(MTYPE_FRR_PTHREAD,
+                                     sizeof(struct frr_pthread));
+                       /* create new thread master */
+                       fpt->master = thread_master_create(attr->name);
+                       /* set attributes */
+                       fpt->attr = *attr;
+                       if (attr == &frr_pthread_attr_default)
+                               fpt->attr.id = frr_pthread_get_id();
+                       /* initialize startup synchronization primitives */
+                       fpt->running_cond_mtx = XCALLOC(
+                               MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
+                       fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
+                                                   sizeof(pthread_cond_t));
+                       pthread_mutex_init(fpt->running_cond_mtx, NULL);
+                       pthread_cond_init(fpt->running_cond, NULL);
+
+                       /* insert into global thread hash */
+                       hash_get(frr_pthread_hash, fpt, hash_alloc_intern);
                }
        }
-       pthread_mutex_unlock(&pthread_table_mtx);
+       pthread_mutex_unlock(&frr_pthread_hash_mtx);
 
        return fpt;
 }
@@ -105,7 +125,11 @@ struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
 void frr_pthread_destroy(struct frr_pthread *fpt)
 {
        thread_master_free(fpt->master);
-       XFREE(MTYPE_FRR_PTHREAD, fpt->name);
+
+       pthread_mutex_destroy(fpt->running_cond_mtx);
+       pthread_cond_destroy(fpt->running_cond);
+       XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
+       XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
        XFREE(MTYPE_FRR_PTHREAD, fpt);
 }
 
@@ -114,74 +138,82 @@ struct frr_pthread *frr_pthread_get(unsigned int id)
        static struct frr_pthread holder = {0};
        struct frr_pthread *fpt;
 
-       pthread_mutex_lock(&pthread_table_mtx);
+       pthread_mutex_lock(&frr_pthread_hash_mtx);
        {
-               holder.id = id;
-               fpt = hash_lookup(pthread_table, &holder);
+               holder.attr.id = id;
+               fpt = hash_lookup(frr_pthread_hash, &holder);
        }
-       pthread_mutex_unlock(&pthread_table_mtx);
+       pthread_mutex_unlock(&frr_pthread_hash_mtx);
 
        return fpt;
 }
 
-int frr_pthread_run(unsigned int id, const pthread_attr_t *attr, void *arg)
+int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
 {
-       struct frr_pthread *fpt = frr_pthread_get(id);
        int ret;
 
-       if (!fpt)
-               return -1;
-
-       ret = pthread_create(&fpt->thread, attr, fpt->start_routine, arg);
+       ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
 
-       /* Per pthread_create(3), the contents of fpt->thread are undefined if
-        * pthread_create() did not succeed. Reset this value to zero. */
+       /*
+        * Per pthread_create(3), the contents of fpt->thread are undefined if
+        * pthread_create() did not succeed. Reset this value to zero.
+        */
        if (ret < 0)
                memset(&fpt->thread, 0x00, sizeof(fpt->thread));
 
        return ret;
 }
 
-/**
- * Calls the stop routine for the frr_pthread and resets any relevant fields.
- *
- * @param fpt - the frr_pthread to stop
- * @param result - pointer to result pointer
- * @return the return code from the stop routine
- */
-static int frr_pthread_stop_actual(struct frr_pthread *fpt, void **result)
+void frr_pthread_wait_running(struct frr_pthread *fpt)
 {
-       int ret = (*fpt->stop_routine)(result, fpt);
-       memset(&fpt->thread, 0x00, sizeof(fpt->thread));
-       return ret;
+       pthread_mutex_lock(fpt->running_cond_mtx);
+       {
+               while (!fpt->running)
+                       pthread_cond_wait(fpt->running_cond,
+                                         fpt->running_cond_mtx);
+       }
+       pthread_mutex_unlock(fpt->running_cond_mtx);
 }
 
-int frr_pthread_stop(unsigned int id, void **result)
+void frr_pthread_notify_running(struct frr_pthread *fpt)
 {
-       struct frr_pthread *fpt = frr_pthread_get(id);
-       return frr_pthread_stop_actual(fpt, result);
+       pthread_mutex_lock(fpt->running_cond_mtx);
+       {
+               fpt->running = true;
+               pthread_cond_signal(fpt->running_cond);
+       }
+       pthread_mutex_unlock(fpt->running_cond_mtx);
 }
 
-/**
+int frr_pthread_stop(struct frr_pthread *fpt, void **result)
+{
+       int ret = (*fpt->attr.stop)(fpt, result);
+       memset(&fpt->thread, 0x00, sizeof(fpt->thread));
+       return ret;
+}
+
+/*
  * Callback for hash_iterate to stop all frr_pthread's.
  */
 static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
 {
        struct frr_pthread *fpt = hb->data;
-       frr_pthread_stop_actual(fpt, NULL);
+       frr_pthread_stop(fpt, NULL);
 }
 
 void frr_pthread_stop_all()
 {
-       pthread_mutex_lock(&pthread_table_mtx);
+       pthread_mutex_lock(&frr_pthread_hash_mtx);
        {
-               hash_iterate(pthread_table, frr_pthread_stop_all_iter, NULL);
+               hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
        }
-       pthread_mutex_unlock(&pthread_table_mtx);
+       pthread_mutex_unlock(&frr_pthread_hash_mtx);
 }
 
 unsigned int frr_pthread_get_id()
 {
+       /* just a sanity check, this should never happen */
+       assert(next_id <= INT_MAX - 1);
        return next_id++;
 }
 
@@ -189,3 +221,60 @@ void frr_pthread_yield(void)
 {
        (void)sched_yield();
 }
+
+/*
+ * ----------------------------------------------------------------------------
+ * Default Event Loop
+ * ----------------------------------------------------------------------------
+ */
+
+/* dummy task for sleeper pipe */
+static int fpt_dummy(struct thread *thread)
+{
+       return 0;
+}
+
+/* poison pill task to end event loop */
+static int fpt_finish(struct thread *thread)
+{
+       struct frr_pthread *fpt = THREAD_ARG(thread);
+       atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+       return 0;
+}
+
+/* stop function, called from other threads to halt this one */
+static int fpt_halt(struct frr_pthread *fpt, void **res)
+{
+       thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
+       pthread_join(fpt->thread, res);
+       fpt = NULL;
+
+       return 0;
+}
+
+/* entry pthread function & main event loop */
+static void *fpt_run(void *arg)
+{
+       struct frr_pthread *fpt = arg;
+       fpt->master->owner = pthread_self();
+
+       int sleeper[2];
+       pipe(sleeper);
+       thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
+
+       fpt->master->handle_signals = false;
+
+       frr_pthread_notify_running(fpt);
+
+       struct thread task;
+       while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
+               if (thread_fetch(fpt->master, &task)) {
+                       thread_call(&task);
+               }
+       }
+
+       close(sleeper[1]);
+       close(sleeper[0]);
+
+       return NULL;
+}
index 7915b43a46d8b59c1d047e1622049d10979baac5..2cc50196a897b81ca103c0c6f3c6ba56314653ae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Utilities and interfaces for managing POSIX threads
+ * Utilities and interfaces for managing POSIX threads within FRR.
  * Copyright (C) 2017  Cumulus Networks
  *
  * This program is free software; you can redistribute it and/or modify
 #define _FRR_PTHREAD_H
 
 #include <pthread.h>
+#include "frratomic.h"
 #include "memory.h"
 #include "thread.h"
 
+DECLARE_MTYPE(FRR_PTHREAD);
 DECLARE_MTYPE(PTHREAD_PRIM);
 
+struct frr_pthread;
+struct frr_pthread_attr;
+
+struct frr_pthread_attr {
+       int id;
+       void *(*start)(void *);
+       int (*stop)(struct frr_pthread *, void **);
+       const char *name;
+};
+
 struct frr_pthread {
 
        /* pthread id */
        pthread_t thread;
 
-       /* frr thread identifier */
-       unsigned int id;
-
        /* thread master for this pthread's thread.c event loop */
        struct thread_master *master;
 
-       /* start routine */
-       void *(*start_routine)(void *);
-
-       /* stop routine */
-       int (*stop_routine)(void **, struct frr_pthread *);
-
-       /* the (hopefully descriptive) name of this thread */
-       char *name;
+       /* caller-specified data; start & stop funcs, name, id */
+       struct frr_pthread_attr attr;
+
+       /*
+        * Notification mechanism for allowing pthreads to notify their parents
+        * when they are ready to do work. This mechanism has two associated
+        * functions:
+        *
+        * - frr_pthread_wait_running()
+        *   This function should be called by the spawning thread after
+        *   frr_pthread_run(). It safely waits until the spawned thread
+        *   indicates that is ready to do work by posting to the condition
+        *   variable.
+        *
+        * - frr_pthread_notify_running()
+        *   This function should be called by the spawned thread when it is
+        *   ready to do work. It will wake up any threads waiting on the
+        *   previously described condition.
+        */
+       pthread_cond_t *running_cond;
+       pthread_mutex_t *running_cond_mtx;
+       _Atomic bool running;
+
+       /*
+        * Fake thread-specific storage. No constraints on usage. Helpful when
+        * creating reentrant pthread implementations. Can be used to pass
+        * argument to pthread entry function.
+        */
+       void *data;
 };
 
-/* Initializes this module.
+extern struct frr_pthread_attr frr_pthread_attr_default;
+
+/*
+ * Initializes this module.
  *
  * Must be called before using any of the other functions.
  */
 void frr_pthread_init(void);
 
-/* Uninitializes this module.
+/*
+ * Uninitializes this module.
  *
  * Destroys all registered frr_pthread's and internal data structures.
  *
@@ -62,34 +96,23 @@ void frr_pthread_init(void);
  */
 void frr_pthread_finish(void);
 
-/* Creates a new frr_pthread.
- *
- * If the provided ID is already assigned to an existing frr_pthread, the
- * return value will be NULL.
- *
- * @param name - the name of the thread. Doesn't have to be unique, but it
- * probably should be. This value is copied and may be safely free'd upon
- * return.
- *
- * @param id - the integral ID of the thread. MUST be unique. The caller may
- * use this id to retrieve the thread.
- *
- * @param start_routine - start routine for the pthread, will be passed to
- * pthread_create (see those docs for details)
+/*
+ * Creates a new frr_pthread with the given attributes.
  *
- * @param stop_routine - stop routine for the pthread, called to terminate the
- * thread. This function should gracefully stop the pthread and clean up any
- * thread-specific resources. The passed pointer is used to return a data
- * result.
+ * The 'attr' argument should be filled out with the desired attributes,
+ * including ID, start and stop functions and the desired name. Alternatively,
+ * if attr is NULL, the default attributes will be used. The pthread will be
+ * set up to run a basic threadmaster loop and the name will be "Anonymous".
+ * Scheduling tasks onto the threadmaster in the 'master' field of the returned
+ * frr_pthread will cause them to run on that pthread.
  *
+ * @param attr - the thread attributes
  * @return the created frr_pthread upon success, or NULL upon failure
  */
-struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
-                                   void *(*start_routine)(void *),
-                                   int (*stop_routine)(void **,
-                                                       struct frr_pthread *));
+struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr);
 
-/* Destroys an frr_pthread.
+/*
+ * Destroys an frr_pthread.
  *
  * Assumes that the associated pthread, if any, has already terminated.
  *
@@ -97,38 +120,66 @@ struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
  */
 void frr_pthread_destroy(struct frr_pthread *fpt);
 
-/* Gets an existing frr_pthread by its id.
+/*
+ * Gets an existing frr_pthread by its id.
  *
  * @return frr_thread associated with the provided id, or NULL on error
  */
 struct frr_pthread *frr_pthread_get(unsigned int id);
 
-/* Creates a new pthread and binds it to a frr_pthread.
+/*
+ * Creates a new pthread and binds it to a frr_pthread.
  *
  * This function is a wrapper for pthread_create. The first parameter is the
  * frr_pthread to bind the created pthread to. All subsequent arguments are
- * passed unmodified to pthread_create().
+ * passed unmodified to pthread_create(). The frr_pthread * provided will be
+ * used as the argument to the pthread entry function. If it is necessary to
+ * pass additional data, the 'data' field in the frr_pthread may be used.
  *
  * This function returns the same code as pthread_create(). If the value is
  * zero, the provided frr_pthread is bound to a running POSIX thread. If the
  * value is less than zero, the provided frr_pthread is guaranteed to be a
  * clean instance that may be susbsequently passed to frr_pthread_run().
  *
- * @param id - frr_pthread to bind the created pthread to
+ * @param fpt - frr_pthread * to run
  * @param attr - see pthread_create(3)
- * @param arg - see pthread_create(3)
  *
  * @return see pthread_create(3)
  */
-int frr_pthread_run(unsigned int id, const pthread_attr_t *attr, void *arg);
+int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr);
+
+/*
+ * Waits until the specified pthread has finished setting up and is ready to
+ * begin work.
+ *
+ * If the pthread's code makes use of the startup synchronization mechanism,
+ * this function should be called before attempting to use the functionality
+ * exposed by the pthread. It waits until the 'running' condition is satisfied
+ * (see struct definition of frr_pthread).
+ *
+ * @param fpt - the frr_pthread * to wait on
+ */
+void frr_pthread_wait_running(struct frr_pthread *fpt);
 
-/* Stops an frr_pthread with a result.
+/*
+ * Notifies other pthreads that the calling thread has finished setting up and
+ * is ready to begin work.
+ *
+ * This will allow any other pthreads waiting in 'frr_pthread_wait_running' to
+ * proceed.
  *
- * @param id - frr_pthread to stop
+ * @param fpt - the frr_pthread * that has finished setting up
+ */
+void frr_pthread_notify_running(struct frr_pthread *fpt);
+
+/*
+ * Stops a frr_pthread with a result.
+ *
+ * @param fpt - frr_pthread * to stop
  * @param result - where to store the thread's result, if any. May be NULL if a
  * result is not needed.
  */
-int frr_pthread_stop(unsigned int id, void **result);
+int frr_pthread_stop(struct frr_pthread *fpt, void **result);
 
 /* Stops all frr_pthread's. */
 void frr_pthread_stop_all(void);
@@ -136,7 +187,8 @@ void frr_pthread_stop_all(void);
 /* Yields the current thread of execution */
 void frr_pthread_yield(void);
 
-/* Returns a unique identifier for use with frr_pthread_new().
+/*
+ * Returns a unique identifier for use with frr_pthread_new().
  *
  * Internally, this is an integer that increments after each call to this
  * function. Because the number of pthreads created should never exceed INT_MAX
index fdcd563a5deded3917e7371f3490add85bf16d58..7866ddb8c446ffa08711d59dc4ce7b1b69c87e32 100644 (file)
--- a/lib/if.c
+++ b/lib/if.c
@@ -210,6 +210,9 @@ void if_delete(struct interface *ifp)
 
        if_link_params_free(ifp);
 
+       if (ifp->desc)
+               XFREE(MTYPE_TMP, ifp->desc);
+
        XFREE(MTYPE_IF, ifp);
 }
 
index bb2c17335429cd2866b474291d5cc0ed71f6eae7..d9a09a3e416df0278b46ab17d4f93565e26009ef 100644 (file)
@@ -715,6 +715,24 @@ DEFUN (accept_lifetime_duration_month_day,
                argv[idx_number_3]->arg);
 }
 
+DEFUN (no_accept_lifetime,
+       no_accept_lifetime_cmd,
+       "no accept-lifetime",
+       NO_STR
+       "Unset accept-lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT_SUB(key, key);
+
+       if (key->accept.start)
+               key->accept.start = 0;
+       if (key->accept.end)
+               key->accept.end = 0;
+       if (key->accept.duration)
+               key->accept.duration = 0;
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (send_lifetime_day_month_day_month,
        send_lifetime_day_month_day_month_cmd,
        "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)",
@@ -925,6 +943,24 @@ DEFUN (send_lifetime_duration_month_day,
                argv[idx_number_3]->arg);
 }
 
+DEFUN (no_send_lifetime,
+       no_send_lifetime_cmd,
+       "no send-lifetime",
+       NO_STR
+       "Unset send-lifetime\n")
+{
+       VTY_DECLVAR_CONTEXT_SUB(key, key);
+
+       if (key->send.start)
+               key->send.start = 0;
+       if (key->send.end)
+               key->send.end = 0;
+       if (key->send.duration)
+               key->send.duration = 0;
+
+       return CMD_SUCCESS;
+}
+
 static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# ",
                                        1};
 
@@ -1047,6 +1083,8 @@ void keychain_init()
                        &accept_lifetime_duration_day_month_cmd);
        install_element(KEYCHAIN_KEY_NODE,
                        &accept_lifetime_duration_month_day_cmd);
+       install_element(KEYCHAIN_KEY_NODE,
+                       &no_accept_lifetime_cmd);
 
        install_element(KEYCHAIN_KEY_NODE,
                        &send_lifetime_day_month_day_month_cmd);
@@ -1064,4 +1102,6 @@ void keychain_init()
                        &send_lifetime_duration_day_month_cmd);
        install_element(KEYCHAIN_KEY_NODE,
                        &send_lifetime_duration_month_day_cmd);
+       install_element(KEYCHAIN_KEY_NODE,
+                       &no_send_lifetime_cmd);
 }
index 6ef00375e8acc479426657c1a720b3962e4605f3..95882c26ece5ff4709421ba0f389526ce31b33a3 100644 (file)
 #define MPLS_MAX_UNRESERVED_LABEL          1048575
 
 /* Default min and max SRGB label range */
-#define MPLS_DEFAULT_MIN_SRGB_LABEL        16000
-#define MPLS_DEFAULT_MAX_SRGB_LABEL        23999
+/* Even if the SRGB allows to manage different Label space between routers,
+ * if an operator want to use the same SRGB for all its router, we must fix
+ * a common range. However, Cisco start its SRGB at 16000 and Juniper ends
+ * its SRGB at 16384 for OSPF. Thus, by fixing the minimum SRGB label to
+ * 8000 we could deal with both Cisco and Juniper.
+ */
+#define MPLS_DEFAULT_MIN_SRGB_LABEL        8000
+#define MPLS_DEFAULT_MAX_SRGB_LABEL        50000
+#define MPLS_DEFAULT_MIN_SRGB_SIZE         5000
+#define MPLS_DEFAULT_MAX_SRGB_SIZE         20000
 
 /* Maximum # labels that can be pushed. */
 #define MPLS_MAX_LABELS                    16
@@ -100,7 +108,8 @@ enum lsp_types_t {
        ZEBRA_LSP_NONE = 0,   /* No LSP. */
        ZEBRA_LSP_STATIC = 1, /* Static LSP. */
        ZEBRA_LSP_LDP = 2,    /* LDP LSP. */
-       ZEBRA_LSP_BGP = 3     /* BGP LSP. */
+       ZEBRA_LSP_BGP = 3,    /* BGP LSP. */
+       ZEBRA_LSP_SR = 4      /* Segment Routing LSP. */
 };
 
 /* Functions for basic label operations. */
index 7e947ea48abc01b05a5a426b7b33ec257677b4e6..bcc2230607802c25d826cacc88d4db56d3b4db37 100644 (file)
 #define ETH_ALEN 6
 #endif
 
-/* for compatibility */
-#ifdef ETHER_ADDR_LEN
-#undef ETHER_ADDR_LEN
-#endif
-#define ETHER_ADDR_LEN 6 CPP_WARN("ETHER_ADDR_LEN is being replaced by ETH_ALEN.\\n")
-
 #define ETHER_ADDR_STRLEN (3*ETH_ALEN)
 /*
  * there isn't a portable ethernet address type. We define our
index 28d26149e5d39afc6f3107c67d8a49d0d4333266..fea5a8cc4015ba6ac858dc1f8a91891b67bcd5e4 100644 (file)
@@ -120,7 +120,7 @@ static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version,
        }
        /* remove leading spaces */
        for (i = j = 0; i < csv_field_len(fld); i++) {
-               if (!isspace(hdr[i])) {
+               if (!isspace((int)hdr[i])) {
                        client_name[j] = hdr[i];
                        j++;
                }
@@ -347,7 +347,7 @@ int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen,
 {
        int rc, len;
        char client_name[32];
-       int cmd_id, type, ver, msglen;
+       int cmd_id = 0, type = 0, ver = 0, msglen = 0;
        csv_t *csv;
        ptm_lib_msg_ctxt_t *p_ctxt = NULL;
 
index 92b7620edac5cd3ab3f76bb7f8dc379b93722557..d7907fafdf7ff26caf858dceb8e043eefeeb971c 100644 (file)
@@ -205,7 +205,7 @@ static const char *timeval_format(struct timeval *tv)
 
        size_t offset = strlen(timebuf);
        snprintf(timebuf + offset, sizeof(timebuf) - offset, ".%ld",
-                tv->tv_usec);
+                (long int)tv->tv_usec);
 
        return timebuf;
 }
index 4026e0cf8ab1a389d3cd7b93fe14526326e560df..43a53b773288266b67f1418669cb7d24f8b49ec6 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -732,7 +732,6 @@ static void vty_end_config(struct vty *vty)
        case KEYCHAIN_NODE:
        case KEYCHAIN_KEY_NODE:
        case MASC_NODE:
-       case PIM_NODE:
        case VTY_NODE:
        case BGP_EVPN_VNI_NODE:
                vty_config_unlock(vty);
@@ -1130,7 +1129,6 @@ static void vty_stop_input(struct vty *vty)
        case KEYCHAIN_NODE:
        case KEYCHAIN_KEY_NODE:
        case MASC_NODE:
-       case PIM_NODE:
        case VTY_NODE:
                vty_config_unlock(vty);
                vty->node = ENABLE_NODE;
index a4fa0966a7c9a8c9d2dbdbb2427d40dae0b0ce08..0c29b523bf7ad7edb6a444bf32e021972420ff6f 100644 (file)
@@ -389,25 +389,28 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
                               vrf_id);
 
        /* Flush all redistribute request. */
-       if (vrf_id == VRF_DEFAULT)
-               for (afi = AFI_IP; afi < AFI_MAX; afi++)
-                       for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
-                               if (zclient->mi_redist[afi][i].enabled) {
-                                       struct listnode *node;
-                                       u_short *id;
-
-                                       for (ALL_LIST_ELEMENTS_RO(
-                                                    zclient->mi_redist[afi][i]
-                                                            .instances,
-                                                    node, id))
-                                               if (!(i == zclient->redist_default
-                                                     && *id == zclient->instance))
-                                                       zebra_redistribute_send(
-                                                               ZEBRA_REDISTRIBUTE_ADD,
-                                                               zclient, afi, i,
-                                                               *id,
-                                                               VRF_DEFAULT);
-                               }
+       if (vrf_id == VRF_DEFAULT) {
+               for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+                       for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+                               if (!zclient->mi_redist[afi][i].enabled)
+                                       continue;
+
+                               struct listnode *node;
+                               u_short *id;
+
+                               for (ALL_LIST_ELEMENTS_RO(
+                                            zclient->mi_redist[afi][i]
+                                            .instances, node, id))
+                                       if (!(i == zclient->redist_default
+                                             && *id == zclient->instance))
+                                               zebra_redistribute_send(
+                                                       ZEBRA_REDISTRIBUTE_ADD,
+                                                       zclient, afi, i,
+                                                       *id,
+                                                       VRF_DEFAULT);
+                       }
+               }
+       }
 
        /* Flush all redistribute request. */
        for (afi = AFI_IP; afi < AFI_MAX; afi++)
@@ -447,29 +450,32 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id)
 
        /* Set unwanted redistribute route. */
        for (afi = AFI_IP; afi < AFI_MAX; afi++)
-               vrf_bitmap_set(zclient->redist[afi][zclient->redist_default],
-                              vrf_id);
+               vrf_bitmap_unset(zclient->redist[afi][zclient->redist_default],
+                                vrf_id);
 
        /* Flush all redistribute request. */
-       if (vrf_id == VRF_DEFAULT)
-               for (afi = AFI_IP; afi < AFI_MAX; afi++)
-                       for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
-                               if (zclient->mi_redist[afi][i].enabled) {
-                                       struct listnode *node;
-                                       u_short *id;
-
-                                       for (ALL_LIST_ELEMENTS_RO(
-                                                    zclient->mi_redist[afi][i]
-                                                            .instances,
-                                                    node, id))
-                                               if (!(i == zclient->redist_default
-                                                     && *id == zclient->instance))
-                                                       zebra_redistribute_send(
-                                                               ZEBRA_REDISTRIBUTE_DELETE,
-                                                               zclient, afi, i,
-                                                               *id,
-                                                               VRF_DEFAULT);
-                               }
+       if (vrf_id == VRF_DEFAULT) {
+               for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+                       for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+                               if (!zclient->mi_redist[afi][i].enabled)
+                                       continue;
+
+                               struct listnode *node;
+                               u_short *id;
+
+                               for (ALL_LIST_ELEMENTS_RO(
+                                            zclient->mi_redist[afi][i]
+                                            .instances, node, id))
+                                       if (!(i == zclient->redist_default
+                                             && *id == zclient->instance))
+                                               zebra_redistribute_send(
+                                                       ZEBRA_REDISTRIBUTE_DELETE,
+                                                       zclient, afi, i,
+                                                       *id,
+                                                       VRF_DEFAULT);
+                       }
+               }
+       }
 
        /* Flush all redistribute request. */
        for (afi = AFI_IP; afi < AFI_MAX; afi++)
@@ -608,6 +614,33 @@ static int zclient_connect(struct thread *t)
        return zclient_start(zclient);
 }
 
+int zclient_send_rnh(struct zclient *zclient, int command, struct prefix *p,
+                    bool exact_match, vrf_id_t vrf_id)
+{
+       struct stream *s;
+
+       s = zclient->obuf;
+       stream_reset(s);
+       zclient_create_header(s, command, vrf_id);
+       stream_putc(s, (exact_match) ? 1 : 0);
+
+       stream_putw(s, PREFIX_FAMILY(p));
+       stream_putc(s, p->prefixlen);
+       switch (PREFIX_FAMILY(p)) {
+       case AF_INET:
+               stream_put_in_addr(s, &p->u.prefix4);
+               break;
+       case AF_INET6:
+               stream_put(s, &(p->u.prefix6), 16);
+               break;
+       default:
+               break;
+       }
+       stream_putw_at(s, 0, stream_get_endp(s));
+
+       return zclient_send_message(zclient);
+}
+
 /*
  * "xdr_encode"-like interface that allows daemon (client) to send
  * a message to zebra server for a route that needs to be
@@ -943,7 +976,7 @@ int zapi_route_encode(u_char cmd, struct stream *s, struct zapi_route *api)
 
                stream_putw(s, api->nexthop_num);
                if (api->nexthop_num)
-                       stream_putw(s, api->nh_vrf_id);
+                       stream_putl(s, api->nh_vrf_id);
 
                for (i = 0; i < api->nexthop_num; i++) {
                        api_nh = &api->nexthops[i];
@@ -1094,7 +1127,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
                }
 
                if (api->nexthop_num)
-                       STREAM_GETW(s, api->nh_vrf_id);
+                       STREAM_GETL(s, api->nh_vrf_id);
 
                for (i = 0; i < api->nexthop_num; i++) {
                        api_nh = &api->nexthops[i];
@@ -1179,6 +1212,72 @@ stream_failure:
        return false;
 }
 
+struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
+{
+       struct nexthop *n = nexthop_new();
+
+       n->type = znh->type;
+       n->ifindex = znh->ifindex;
+       n->gate = znh->gate;
+
+       /*
+        * This function does not currently handle labels
+        */
+
+       return n;
+}
+
+bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
+{
+       uint32_t i;
+
+       memset(nhr, 0, sizeof(*nhr));
+
+       STREAM_GETW(s, nhr->prefix.family);
+       STREAM_GETC(s, nhr->prefix.prefixlen);
+       switch(nhr->prefix.family) {
+       case AF_INET:
+               STREAM_GET(&nhr->prefix.u.prefix4.s_addr, s, IPV4_MAX_BYTELEN);
+               break;
+       case AF_INET6:
+               STREAM_GET(&nhr->prefix.u.prefix6, s, IPV6_MAX_BYTELEN);
+               break;
+       default:
+               break;
+       }
+
+       STREAM_GETC(s, nhr->distance);
+       STREAM_GETL(s, nhr->metric);
+       STREAM_GETC(s, nhr->nexthop_num);
+
+       for (i = 0; i < nhr->nexthop_num ; i++) {
+               STREAM_GETC(s, nhr->nexthops[i].type);
+               switch (nhr->nexthops[i].type) {
+               case NEXTHOP_TYPE_IPV4:
+               case NEXTHOP_TYPE_IPV4_IFINDEX:
+                       STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr,
+                                  s, IPV4_MAX_BYTELEN);
+                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
+                       break;
+               case NEXTHOP_TYPE_IFINDEX:
+                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
+                       break;
+               case NEXTHOP_TYPE_IPV6:
+               case NEXTHOP_TYPE_IPV6_IFINDEX:
+                       STREAM_GET(&nhr->nexthops[i].gate.ipv6,
+                                  s, IPV6_MAX_BYTELEN);
+                       STREAM_GETL(s, nhr->nexthops[i].ifindex);
+                       break;
+               case NEXTHOP_TYPE_BLACKHOLE:
+                       break;
+               }
+       }
+
+       return true;
+stream_failure:
+       return false;
+}
+
 /*
  * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
  * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
index 8554f3e39dc528c051cab571d503fe3899e25b11..5c7c5d6d5b0ca079ac76be6e5b4e96bb0800f411 100644 (file)
@@ -489,10 +489,16 @@ extern int zapi_ipv4_route_ipv6_nexthop(u_char, struct zclient *,
                                        struct zapi_ipv6 *)
        __attribute__((deprecated));
 extern int zclient_route_send(u_char, struct zclient *, struct zapi_route *);
+extern int zclient_send_rnh(struct zclient *zclient, int command,
+                           struct prefix *p, bool exact_match,
+                           vrf_id_t vrf_id);
 extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
 extern int zapi_route_decode(struct stream *, struct zapi_route *);
 bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
                              enum zapi_route_notify_owner *note);
+extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
+extern bool zapi_nexthop_update_decode(struct stream *s,
+                                      struct zapi_route *nhr);
 
 static inline void zapi_route_set_blackhole(struct zapi_route *api,
                                            enum blackhole_type bh_type)
index 252e4a454549edacefec34a13611cf9d931fd3a2..ed624c6ae414a775fd35d2f8cc2192383c5bd278 100644 (file)
@@ -221,6 +221,7 @@ struct ospf6_area *ospf6_area_create(u_int32_t area_id, struct ospf6 *o, int df)
        oa->lsdb->hook_add = ospf6_area_lsdb_hook_add;
        oa->lsdb->hook_remove = ospf6_area_lsdb_hook_remove;
        oa->lsdb_self = ospf6_lsdb_create(oa);
+       oa->temp_router_lsa_lsdb = ospf6_lsdb_create(oa);
 
        oa->spf_table = OSPF6_ROUTE_TABLE_CREATE(AREA, SPF_RESULTS);
        oa->spf_table->scope = oa;
@@ -279,6 +280,7 @@ void ospf6_area_delete(struct ospf6_area *oa)
 
        ospf6_lsdb_delete(oa->lsdb);
        ospf6_lsdb_delete(oa->lsdb_self);
+       ospf6_lsdb_delete(oa->temp_router_lsa_lsdb);
 
        ospf6_spf_table_finish(oa->spf_table);
        ospf6_route_table_delete(oa->spf_table);
index d212d9238726971f2e5f2e533aa21aedd189d99a..b7cd9b4b09af703a939abb1284110c5841636286 100644 (file)
@@ -55,6 +55,7 @@ struct ospf6_area {
 
        struct ospf6_lsdb *lsdb;
        struct ospf6_lsdb *lsdb_self;
+       struct ospf6_lsdb *temp_router_lsa_lsdb;
 
        struct ospf6_route_table *spf_table;
        struct ospf6_route_table *route_table;
index 745b87b890ed13f527557a19b37dd31a99e09f7d..02f8eb0b09ca6f2cd1b7e7accab7fb4d4fe1b497 100644 (file)
@@ -618,6 +618,56 @@ static void ospf6_asbr_routemap_unset(int type)
        ospf6->rmap[type].map = NULL;
 }
 
+static int ospf6_asbr_routemap_update_timer(struct thread *thread)
+{
+       void **arg;
+       int arg_type;
+
+       arg = THREAD_ARG(thread);
+       arg_type = (int)(intptr_t)arg[1];
+
+       ospf6->t_distribute_update = NULL;
+
+       if (ospf6->rmap[arg_type].name)
+               ospf6->rmap[arg_type].map = route_map_lookup_by_name(
+                                       ospf6->rmap[arg_type].name);
+       if (ospf6->rmap[arg_type].map) {
+               if (IS_OSPF6_DEBUG_ASBR)
+                       zlog_debug("%s: route-map %s update, reset redist %s",
+                                  __PRETTY_FUNCTION__,
+                                  ospf6->rmap[arg_type].name,
+                                  ZROUTE_NAME(arg_type));
+
+               ospf6_zebra_no_redistribute(arg_type);
+               ospf6_zebra_redistribute(arg_type);
+       }
+
+       XFREE(MTYPE_OSPF6_DIST_ARGS, arg);
+       return 0;
+}
+
+void ospf6_asbr_distribute_list_update(int type)
+{
+       void **args = NULL;
+
+       if (ospf6->t_distribute_update)
+               return;
+
+       args = XCALLOC(MTYPE_OSPF6_DIST_ARGS, sizeof(void *)*2);
+
+       args[0] = ospf6;
+       args[1] = (void *)((ptrdiff_t)type);
+
+       if (IS_OSPF6_DEBUG_ASBR)
+               zlog_debug("%s: trigger redistribute %s reset thread",
+                          __PRETTY_FUNCTION__, ZROUTE_NAME(type));
+
+       ospf6->t_distribute_update = NULL;
+       thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer,
+                             (void **)args, OSPF_MIN_LS_INTERVAL,
+                             &ospf6->t_distribute_update);
+}
+
 static void ospf6_asbr_routemap_update(const char *mapname)
 {
        int type;
@@ -636,15 +686,27 @@ static void ospf6_asbr_routemap_update(const char *mapname)
                                        zlog_debug("%s: route-map %s update, reset redist %s",
                                                   __PRETTY_FUNCTION__, mapname,
                                                   ZROUTE_NAME(type));
-
-                               ospf6_zebra_no_redistribute(type);
-                               ospf6_zebra_redistribute(type);
+                               ospf6_asbr_distribute_list_update(type);
                        }
                } else
                        ospf6->rmap[type].map = NULL;
        }
 }
 
+static void ospf6_asbr_routemap_event(route_map_event_t event, const char *name)
+{
+       int type;
+
+       if (ospf6 == NULL)
+               return;
+       for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+               if ((ospf6->rmap[type].name) &&
+                   (strcmp(ospf6->rmap[type].name, name) == 0)) {
+                       ospf6_asbr_distribute_list_update(type);
+               }
+       }
+}
+
 int ospf6_asbr_is_asbr(struct ospf6 *o)
 {
        return o->external_table->count;
@@ -745,7 +807,6 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
        match = ospf6_route_lookup(prefix, ospf6->external_table);
        if (match) {
                info = match->route_option;
-
                /* copy result of route-map */
                if (ospf6->rmap[type].map) {
                        if (troute.path.metric_type)
@@ -779,7 +840,9 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
                if (IS_OSPF6_DEBUG_ASBR) {
                        inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf,
                                  sizeof(ibuf));
-                       zlog_debug("Advertise as AS-External Id:%s", ibuf);
+                       prefix2str(prefix, pbuf, sizeof(pbuf));
+                       zlog_debug("Advertise as AS-External Id:%s prefix %s metric %u",
+                                  ibuf, pbuf, match->path.metric_type);
                }
 
                match->path.origin.id = htonl(info->id);
@@ -830,7 +893,9 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
 
        if (IS_OSPF6_DEBUG_ASBR) {
                inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf));
-               zlog_debug("Advertise as AS-External Id:%s", ibuf);
+               prefix2str(prefix, pbuf, sizeof(pbuf));
+               zlog_debug("Advertise as AS-External Id:%s prefix %s metric %u",
+                          ibuf, pbuf, route->path.metric_type);
        }
 
        route->path.origin.id = htonl(info->id);
@@ -1339,6 +1404,7 @@ static void ospf6_routemap_init(void)
 
        route_map_add_hook(ospf6_asbr_routemap_update);
        route_map_delete_hook(ospf6_asbr_routemap_update);
+       route_map_event_hook(ospf6_asbr_routemap_event);
 
        route_map_set_metric_hook(generic_set_add);
        route_map_no_set_metric_hook(generic_set_delete);
@@ -1538,6 +1604,10 @@ void ospf6_asbr_redistribute_reset(void)
 
 void ospf6_asbr_terminate(void)
 {
+       /* Cleanup route maps */
+       route_map_add_hook(NULL);
+       route_map_delete_hook(NULL);
+       route_map_event_hook(NULL);
        route_map_finish();
 }
 
index 7f4665ac2b501645831f610dbbdd2bbb1efba7d2..cc4f0272aabbfc4d33c6710cb1253df341760af3 100644 (file)
@@ -95,5 +95,6 @@ extern int config_write_ospf6_debug_asbr(struct vty *vty);
 extern void install_element_ospf6_debug_asbr(void);
 extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
                                              struct ospf6_route *route);
+extern void ospf6_asbr_distribute_list_update(int type);
 
 #endif /* OSPF6_ASBR_H */
index 82f75b153eb204882b30faace2bd1d3bd501f8a0..cca4616c163323b6385d967e5d93d2639c7770e9 100644 (file)
@@ -191,7 +191,7 @@ int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2)
 
 /* ospf6 age functions */
 /* calculate birth */
-static void ospf6_lsa_age_set(struct ospf6_lsa *lsa)
+void ospf6_lsa_age_set(struct ospf6_lsa *lsa)
 {
        struct timeval now;
 
index 3536d33d19ab74b1fc57027a38bf1a9f32a57506..db446a328734f4ec95aec29afbbea974f01252bb 100644 (file)
@@ -252,5 +252,6 @@ extern void ospf6_lsa_terminate(void);
 
 extern int config_write_ospf6_debug_lsa(struct vty *vty);
 extern void install_element_ospf6_debug_lsa(void);
+extern void ospf6_lsa_age_set(struct ospf6_lsa *lsa);
 
 #endif /* OSPF6_LSA_H */
index 9a6729ee2a6aee8f820c43b95f92fa428234511a..88f03d8f646ec1d0d49b0bdc07e8cd3815e1f280 100644 (file)
@@ -97,6 +97,14 @@ static void __attribute__((noreturn)) ospf6_exit(int status)
        ospf6_asbr_terminate();
        ospf6_lsa_terminate();
 
+       /* reverse access_list_init */
+       access_list_reset();
+
+       /* reverse prefix_list_init */
+       prefix_list_add_hook(NULL);
+       prefix_list_delete_hook(NULL);
+       prefix_list_reset();
+
        vrf_terminate();
 
        if (zclient) {
index 1c3523b43d327fa76a4a21d0cded83a6b13967bd..c008b54ce7b765a3502f6b0da358f6fdfaa93df9 100644 (file)
@@ -42,4 +42,5 @@ DEFINE_MTYPE(OSPF6D, OSPF6_SPFTREE, "OSPF6 SPF tree")
 DEFINE_MTYPE(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop")
 DEFINE_MTYPE(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info")
 DEFINE_MTYPE(OSPF6D, OSPF6_PATH, "OSPF6 Path")
+DEFINE_MTYPE(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments")
 DEFINE_MTYPE(OSPF6D, OSPF6_OTHER, "OSPF6 other")
index 548af5e321e7767ae7c60187b0ff977454cf9a8e..a97d677543f3a189492e6e4b79d94ce89805410a 100644 (file)
@@ -41,6 +41,7 @@ DECLARE_MTYPE(OSPF6_SPFTREE)
 DECLARE_MTYPE(OSPF6_NEXTHOP)
 DECLARE_MTYPE(OSPF6_EXTERNAL_INFO)
 DECLARE_MTYPE(OSPF6_PATH)
+DECLARE_MTYPE(OSPF6_DIST_ARGS)
 DECLARE_MTYPE(OSPF6_OTHER)
 
 #endif /* _QUAGGA_OSPF6_MEMORY_H */
index 6e6d8a7f009756a54234a72acf5edb840ba1be56..17ce1771e2a76a3d4b23049ebc5c8e26c46f907b 100644 (file)
@@ -163,21 +163,20 @@ static void ospf6_vertex_delete(struct ospf6_vertex *v)
 }
 
 static struct ospf6_lsa *ospf6_lsdesc_lsa(caddr_t lsdesc,
-                                         struct ospf6_vertex *v,
-                                         uint32_t link_id)
+                                         struct ospf6_vertex *v)
 {
-       struct ospf6_lsa *lsa;
+       struct ospf6_lsa *lsa = NULL;
        u_int16_t type = 0;
        u_int32_t id = 0, adv_router = 0;
 
        if (VERTEX_IS_TYPE(NETWORK, v)) {
                type = htons(OSPF6_LSTYPE_ROUTER);
-               id = link_id;
+               id = htonl(0);
                adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc);
        } else {
                if (ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, lsdesc)) {
                        type = htons(OSPF6_LSTYPE_ROUTER);
-                       id = link_id;
+                       id = htonl(0);
                        adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc);
                } else if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, lsdesc)) {
                        type = htons(OSPF6_LSTYPE_NETWORK);
@@ -186,19 +185,22 @@ static struct ospf6_lsa *ospf6_lsdesc_lsa(caddr_t lsdesc,
                }
        }
 
-       lsa = ospf6_lsdb_lookup(type, id, adv_router, v->area->lsdb);
-
+       if (type == htons(OSPF6_LSTYPE_NETWORK))
+               lsa = ospf6_lsdb_lookup(type, id, adv_router, v->area->lsdb);
+       else
+               lsa = ospf6_create_single_router_lsa(v->area, v->area->lsdb,
+                                                    adv_router);
        if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
                char ibuf[16], abuf[16];
                inet_ntop(AF_INET, &id, ibuf, sizeof(ibuf));
                inet_ntop(AF_INET, &adv_router, abuf, sizeof(abuf));
                if (lsa)
-                       zlog_debug("  Link to: %s , V %s id %u", lsa->name,
-                                  v->name, link_id);
+                       zlog_debug("  Link to: %s len %u, V %s", lsa->name,
+                                  ntohs(lsa->header->length), v->name);
                else
-                       zlog_debug("  Link to: [%s Id:%s Adv:%s] No LSA , V %s id %u",
+                       zlog_debug("  Link to: [%s Id:%s Adv:%s] No LSA , V %s",
                                   ospf6_lstype_name(type), ibuf, abuf,
-                                  v->name, link_id);
+                                  v->name);
        }
 
        return lsa;
@@ -461,17 +463,14 @@ void ospf6_spf_calculation(u_int32_t router_id,
        struct ospf6_vertex *root, *v, *w;
        int size;
        caddr_t lsdesc;
-       struct ospf6_lsa *lsa, *self_rtr_lsa = NULL, *rtr_lsa = NULL;
-       const struct route_node *end = NULL;
+       struct ospf6_lsa *lsa;
        struct in6_addr address;
-       struct ospf6_lsdb *lsdb = NULL;
 
        ospf6_spf_table_finish(result_table);
 
        /* Install the calculating router itself as the root of the SPF tree */
        /* construct root vertex */
-       lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_ROUTER), htonl(0), router_id,
-                               oa->lsdb_self);
+       lsa = ospf6_create_single_router_lsa(oa, oa->lsdb_self, router_id);
        if (lsa == NULL) {
                if (IS_OSPF6_DEBUG_SPF(PROCESS))
                        zlog_debug("%s: No router LSA for area %s\n", __func__,
@@ -479,8 +478,6 @@ void ospf6_spf_calculation(u_int32_t router_id,
                return;
        }
 
-       self_rtr_lsa = lsa;
-
        /* initialize */
        candidate_list = pqueue_create();
        candidate_list->cmp = ospf6_vertex_cmp;
@@ -510,139 +507,63 @@ void ospf6_spf_calculation(u_int32_t router_id,
                     && ospf6_router_is_stub_router(v->lsa)))
                        continue;
 
-               if (VERTEX_IS_TYPE(ROUTER, v)) {
-                       /* First fetch root Router LSAs from lsdb_self */
-                       if (v->lsa == self_rtr_lsa)
-                               lsdb = oa->lsdb_self;
-                       else
-                               lsdb = v->area->lsdb;
-
-                       /* Iterating multiple ROUTER LSAs from same adv router
-                        * with different Link State ID */
-                       end = ospf6_lsdb_head(lsdb, 2,
-                                       htons(OSPF6_LSTYPE_ROUTER),
-                                       v->lsa->header->adv_router,
-                                       &rtr_lsa);
-                       while (rtr_lsa) {
-                               if (IS_OSPF6_DEBUG_SPF(PROCESS))
-                                       zlog_debug("%s: Next LSA %s to process"
-                                                  ,__PRETTY_FUNCTION__,
-                                                 rtr_lsa->name);
-                               size = sizeof(struct ospf6_router_lsdesc);
-                               /* For each LS description in the just-added vertex V's LSA */
-                               for (lsdesc = OSPF6_LSA_HEADER_END(
-                                                       rtr_lsa->header) + 4;
-                                    lsdesc + size <= OSPF6_LSA_END(
-                                                       rtr_lsa->header);
-                                    lsdesc += size) {
-                                       lsa = ospf6_lsdesc_lsa(lsdesc, v,
-                                               rtr_lsa->header->id);
-                                       if (lsa == NULL)
-                                               continue;
-
-                                       if (OSPF6_LSA_IS_MAXAGE(lsa))
-                                               continue;
-
-                                       if (!ospf6_lsdesc_backlink(lsa,
-                                                                  lsdesc, v))
-                                               continue;
-
-                                       w = ospf6_vertex_create(lsa);
-                                       w->area = oa;
-                                       w->parent = v;
-                                       w->link_id = rtr_lsa->header->id;
-
-                                       if (VERTEX_IS_TYPE(ROUTER, v)) {
-                                               w->cost = v->cost
-                                               + ROUTER_LSDESC_GET_METRIC(lsdesc);
-                                               w->hops =
-                                                       v->hops
-                                                       + (VERTEX_IS_TYPE(NETWORK, w)
-                                                          ? 0 : 1);
-                                       } else /* NETWORK */ {
-                                               w->cost = v->cost;
-                                               w->hops = v->hops + 1;
-                                       }
-
-                                       /* nexthop calculation */
-                                       if (w->hops == 0)
-                                               ospf6_add_nexthop(w->nh_list,
-                                                       ROUTER_LSDESC_GET_IFID(lsdesc)
-                                                       , NULL);
-                                       else if (w->hops == 1 && v->hops == 0)
-                                               ospf6_nexthop_calc(w, v, lsdesc);
-                                       else {
-                                               ospf6_copy_nexthops(w->nh_list,
-                                                                   v->nh_list);
-                                       }
-
-                                       /* add new candidate to the candidate_list */
-                                       if (IS_OSPF6_DEBUG_SPF(PROCESS))
-                                               zlog_debug(
-                                                       "  New candidate: %s hops %d cost %d",
-                                                       w->name, w->hops,
-                                                       w->cost);
-                                       pqueue_enqueue(w, candidate_list);
-                               }
-                               /* Fetch next Link state ID Router LSA */
-                               rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
-                       }
-               } else {
-                       /* For each LS description in the just-added vertex V's LSA */
-                       size = (VERTEX_IS_TYPE(ROUTER, v)
-                                       ? sizeof(struct ospf6_router_lsdesc)
-                                       : sizeof(struct ospf6_network_lsdesc));
-                       for (lsdesc = OSPF6_LSA_HEADER_END(v->lsa->header) + 4;
-                            lsdesc + size <= OSPF6_LSA_END(v->lsa->header);
-                            lsdesc += size) {
-                               lsa = ospf6_lsdesc_lsa(lsdesc, v, v->link_id);
-                               if (lsa == NULL)
-                                       continue;
-
-                               if (OSPF6_LSA_IS_MAXAGE(lsa))
-                                       continue;
-
-                               if (!ospf6_lsdesc_backlink(lsa, lsdesc, v))
-                                       continue;
-
-                               w = ospf6_vertex_create(lsa);
-                               w->area = oa;
-                               w->parent = v;
-                               if (VERTEX_IS_TYPE(ROUTER, v)) {
-                                       w->cost = v->cost
+               /* For each LS description in the just-added vertex V's LSA */
+               size = (VERTEX_IS_TYPE(ROUTER, v)
+                               ? sizeof(struct ospf6_router_lsdesc)
+                               : sizeof(struct ospf6_network_lsdesc));
+               for (lsdesc = OSPF6_LSA_HEADER_END(v->lsa->header) + 4;
+                    lsdesc + size <= OSPF6_LSA_END(v->lsa->header);
+                    lsdesc += size) {
+                       lsa = ospf6_lsdesc_lsa(lsdesc, v);
+                       if (lsa == NULL)
+                               continue;
+
+                       if (OSPF6_LSA_IS_MAXAGE(lsa))
+                               continue;
+
+                       if (!ospf6_lsdesc_backlink(lsa, lsdesc, v))
+                               continue;
+
+                       w = ospf6_vertex_create(lsa);
+                       w->area = oa;
+                       w->parent = v;
+                       if (VERTEX_IS_TYPE(ROUTER, v)) {
+                               w->cost = v->cost
                                          + ROUTER_LSDESC_GET_METRIC(lsdesc);
-                                       w->hops =
-                                               v->hops
-                                               + (VERTEX_IS_TYPE(NETWORK, w) ?
-                                                  0 : 1);
-                               } else /* NETWORK */ {
-                                       w->cost = v->cost;
-                                       w->hops = v->hops + 1;
-                               }
-
-                               /* nexthop calculation */
-                               if (w->hops == 0)
-                                       ospf6_add_nexthop(w->nh_list,
+                               w->hops =
+                                       v->hops
+                                       + (VERTEX_IS_TYPE(NETWORK, w) ? 0 : 1);
+                       } else {
+                               /* NETWORK */
+                               w->cost = v->cost;
+                               w->hops = v->hops + 1;
+                       }
+
+                       /* nexthop calculation */
+                       if (w->hops == 0)
+                               ospf6_add_nexthop(
+                                       w->nh_list,
                                        ROUTER_LSDESC_GET_IFID(lsdesc), NULL);
-                               else if (w->hops == 1 && v->hops == 0)
-                                       ospf6_nexthop_calc(w, v, lsdesc);
-                               else {
-                                       ospf6_copy_nexthops(w->nh_list,
-                                                           v->nh_list);
-                               }
-
-                               /* add new candidate to the candidate_list */
-                               if (IS_OSPF6_DEBUG_SPF(PROCESS))
-                                       zlog_debug(
+                       else if (w->hops == 1 && v->hops == 0)
+                               ospf6_nexthop_calc(w, v, lsdesc);
+                       else
+                               ospf6_copy_nexthops(w->nh_list, v->nh_list);
+
+
+                       /* add new candidate to the candidate_list */
+                       if (IS_OSPF6_DEBUG_SPF(PROCESS))
+                               zlog_debug(
                                        "  New candidate: %s hops %d cost %d",
                                                w->name, w->hops, w->cost);
-                               pqueue_enqueue(w, candidate_list);
-                       }
+                       pqueue_enqueue(w, candidate_list);
                }
        }
 
+
        pqueue_delete(candidate_list);
 
+       ospf6_remove_temp_router_lsa(oa);
+
        oa->spf_calculation++;
 }
 
@@ -1029,3 +950,153 @@ void ospf6_spf_init(void)
        install_element(OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
        install_element(OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
 }
+
+/* Create Aggregated Large Router-LSA from multiple Link-State IDs
+ * RFC 5340 A 4.3:
+ * When more than one router-LSA is received from a single router,
+ * the links are processed as if concatenated into a single LSA.*/
+struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
+                                                struct ospf6_lsdb *lsdb,
+                                                uint32_t adv_router)
+{
+       struct ospf6_lsa *lsa = NULL;
+       struct ospf6_lsa *rtr_lsa = NULL;
+       struct ospf6_lsa_header *lsa_header = NULL;
+       uint8_t *new_header = NULL;
+       const struct route_node *end = NULL;
+       uint16_t lsa_length, total_lsa_length = 0, num_lsa = 0;
+       u_int16_t type = 0;
+       char ifbuf[16];
+       uint32_t interface_id;
+       caddr_t lsd;
+
+       lsa_length = sizeof(struct ospf6_lsa_header) +
+                               sizeof(struct ospf6_router_lsa);
+       total_lsa_length = lsa_length;
+       type = htons(OSPF6_LSTYPE_ROUTER);
+
+       /* First check Aggregated LSA formed earlier in Cache */
+       lsa = ospf6_lsdb_lookup(type, htonl(0), adv_router,
+                               area->temp_router_lsa_lsdb);
+       if (lsa)
+               return lsa;
+
+       inet_ntop(AF_INET, &adv_router, ifbuf, sizeof(ifbuf));
+
+       /* Determine total LSA length from all link state ids */
+       end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
+       while (rtr_lsa) {
+               lsa = rtr_lsa;
+               if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+                       rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+                       continue;
+               }
+               lsa_header = (struct ospf6_lsa_header *) rtr_lsa->header;
+               total_lsa_length += (ntohs(lsa_header->length)
+                                    - lsa_length);
+               num_lsa++;
+               rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+       }
+       if (IS_OSPF6_DEBUG_SPF(PROCESS))
+               zlog_debug("%s: adv_router %s num_lsa %u to convert.",
+                       __PRETTY_FUNCTION__, ifbuf, num_lsa);
+       if (num_lsa == 1)
+               return lsa;
+
+       if (num_lsa == 0) {
+               if (IS_OSPF6_DEBUG_SPF(PROCESS))
+                       zlog_debug("%s: adv_router %s not found in LSDB.",
+                                  __PRETTY_FUNCTION__, ifbuf);
+               return NULL;
+       }
+
+       /* Allocate memory for this LSA */
+       new_header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, total_lsa_length);
+       if (!new_header)
+               return NULL;
+
+       /* LSA information structure */
+       lsa = (struct ospf6_lsa *)XCALLOC(MTYPE_OSPF6_LSA,
+                                         sizeof(struct ospf6_lsa));
+       if (!lsa) {
+               free(new_header);
+               return NULL;
+       }
+
+       lsa->header = (struct ospf6_lsa_header *)new_header;
+
+       lsa->lsdb = area->temp_router_lsa_lsdb;
+
+       /* Fill Larger LSA Payload */
+       end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
+       if (rtr_lsa) {
+               if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+                       /* Append first Link State ID LSA */
+                       lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
+                       memcpy(new_header, lsa_header,
+                               ntohs(lsa_header->length));
+                       /* Assign new lsa length as aggregated length. */
+                       ((struct ospf6_lsa_header *)new_header)->length =
+                                       htons(total_lsa_length);
+                       new_header += ntohs(lsa_header->length);
+                       num_lsa--;
+               }
+       }
+
+       /* Print LSA Name */
+       ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
+
+       rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+       while (rtr_lsa) {
+               if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+                       rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+                       continue;
+               }
+
+               if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
+                       lsd = OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4;
+                       interface_id = ROUTER_LSDESC_GET_IFID(lsd);
+                       inet_ntop(AF_INET, &interface_id, ifbuf, sizeof(ifbuf));
+                       zlog_debug("%s: Next Router LSA %s to aggreat with len %u interface_id %s",
+                                  __PRETTY_FUNCTION__, rtr_lsa->name,
+                                  ntohs(lsa_header->length), ifbuf);
+               }
+
+               /* Append Next Link State ID LSA */
+               lsa_header = (struct ospf6_lsa_header *) rtr_lsa->header;
+               memcpy(new_header, (OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4),
+                      (ntohs(lsa_header->length) - lsa_length));
+               new_header += (ntohs(lsa_header->length) - lsa_length);
+               num_lsa--;
+
+               rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+       }
+
+       /* Calculate birth of this lsa */
+       ospf6_lsa_age_set(lsa);
+
+       /* Store Aggregated LSA into area temp lsdb */
+       ospf6_lsdb_add(lsa, area->temp_router_lsa_lsdb);
+
+       if (IS_OSPF6_DEBUG_SPF(PROCESS))
+               zlog_debug("%s: LSA %s id %u type 0%x len %u num_lsa %u",
+                          __PRETTY_FUNCTION__, lsa->name,
+                          ntohl(lsa->header->id), ntohs(lsa->header->type),
+                          ntohs(lsa->header->length), num_lsa);
+
+       return lsa;
+}
+
+void ospf6_remove_temp_router_lsa(struct ospf6_area *area)
+{
+       struct ospf6_lsa *lsa = NULL;
+
+       for (ALL_LSDB(area->temp_router_lsa_lsdb, lsa)) {
+               if (IS_OSPF6_DEBUG_SPF(PROCESS))
+                       zlog_debug("%s Remove LSA %s lsa->lock %u lsdb count %u",
+                                  __PRETTY_FUNCTION__,
+                                  lsa->name, lsa->lock,
+                                  area->temp_router_lsa_lsdb->count);
+               ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb);
+       }
+}
index dbb88d12ba0089525e7dda04f8ad9066b774af79..f294b8d34f33d9b747c2f77d7d66d7c964dfc39d 100644 (file)
@@ -149,5 +149,9 @@ extern int config_write_ospf6_debug_spf(struct vty *vty);
 extern void install_element_ospf6_debug_spf(void);
 extern void ospf6_spf_init(void);
 extern void ospf6_spf_reason_string(unsigned int reason, char *buf, int size);
+extern struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
+                                                       struct ospf6_lsdb *lsdb,
+                                                       uint32_t adv_router);
+extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area);
 
 #endif /* OSPF6_SPF_H */
index 5d1144335be4c27b55d1f6a4bdf96f0017e59167..749873bcf8c753dae2232b3d2125e0b97df36b3c 100644 (file)
@@ -224,6 +224,7 @@ static void ospf6_disable(struct ospf6 *o)
                THREAD_OFF(o->maxage_remover);
                THREAD_OFF(o->t_spf_calc);
                THREAD_OFF(o->t_ase_calc);
+               THREAD_OFF(o->t_distribute_update);
        }
 }
 
index d8d34d0f3c7ff993a528511818e9e3f987fd8f7c..b39c25ba84c46472f9d4ba5dbd1e446a3f6bb830 100644 (file)
@@ -82,6 +82,7 @@ struct ospf6 {
        struct thread *t_spf_calc; /* SPF calculation timer. */
        struct thread *t_ase_calc; /* ASE calculation timer. */
        struct thread *maxage_remover;
+       struct thread *t_distribute_update; /* Distirbute update timer. */
 
        u_int32_t ref_bandwidth;
 
index d28d9dd06417010f54e60b9add8fe9d543f7cdfe..bbc1cc18f6ddac69e710075a1dbfc7b385c1ad09 100644 (file)
@@ -360,6 +360,49 @@ DEFUN (show_ipv6_ospf6_database_router,
        return CMD_SUCCESS;
 }
 
+DEFUN_HIDDEN (show_ipv6_ospf6_database_aggr_router,
+       show_ipv6_ospf6_database_aggr_router_cmd,
+       "show ipv6 ospf6 database aggr adv-router A.B.C.D",
+       SHOW_STR
+       IPV6_STR
+       OSPF6_STR
+       "Display Link state database\n"
+       "Aggregated Router LSA\n"
+       "Search by Advertising Router\n"
+       "Specify Advertising Router as IPv4 address notation\n")
+{
+       int level = OSPF6_LSDB_SHOW_LEVEL_DETAIL;
+       uint16_t type = htons(OSPF6_LSTYPE_ROUTER);
+       int idx_ipv4 = 6;
+       struct listnode *i;
+       struct ospf6 *o = ospf6;
+       struct ospf6_area *oa;
+       struct ospf6_lsdb *lsdb;
+       uint32_t adv_router = 0;
+
+       inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router);
+
+       for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) {
+               if (adv_router == o->router_id)
+                       lsdb = oa->lsdb_self;
+               else
+                       lsdb = oa->lsdb;
+               if (ospf6_create_single_router_lsa(oa, lsdb,
+                                                  adv_router) == NULL) {
+                       vty_out(vty, "Adv router is not found in LSDB.");
+                       return CMD_SUCCESS;
+               }
+               ospf6_lsdb_show(vty, level, &type, NULL, NULL,
+                               oa->temp_router_lsa_lsdb);
+               /* Remove the temp cache */
+               ospf6_remove_temp_router_lsa(oa);
+       }
+
+       vty_out(vty, "\n");
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (show_ipv6_ospf6_database_type_id,
        show_ipv6_ospf6_database_type_id_cmd,
        "show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> linkstate-id A.B.C.D [<detail|dump|internal>]",
@@ -1219,6 +1262,7 @@ void ospf6_init(void)
        install_element(
                VIEW_NODE,
                &show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd);
+       install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd);
 
        /* Make ospf protocol socket. */
        ospf6_serv_sock();
index 6a410f4ed3fd4d831a37754587f5bdd5096a210d..66ab59b816e494601d85dc9371fa8dc24297d396 100644 (file)
@@ -51,6 +51,8 @@ unsigned long conf_debug_ospf_lsa = 0;
 unsigned long conf_debug_ospf_zebra = 0;
 unsigned long conf_debug_ospf_nssa = 0;
 unsigned long conf_debug_ospf_te = 0;
+unsigned long conf_debug_ospf_ext = 0;
+unsigned long conf_debug_ospf_sr = 0;
 
 /* Enable debug option variables -- valid only session. */
 unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
@@ -61,7 +63,8 @@ unsigned long term_debug_ospf_lsa = 0;
 unsigned long term_debug_ospf_zebra = 0;
 unsigned long term_debug_ospf_nssa = 0;
 unsigned long term_debug_ospf_te = 0;
-
+unsigned long term_debug_ospf_ext = 0;
+unsigned long term_debug_ospf_sr = 0;
 
 const char *ospf_redist_string(u_int route_type)
 {
@@ -1441,6 +1444,33 @@ DEFUN (no_debug_ospf_te,
        return CMD_SUCCESS;
 }
 
+DEFUN (debug_ospf_sr,
+       debug_ospf_sr_cmd,
+       "debug ospf sr",
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-SR information\n")
+{
+       if (vty->node == CONFIG_NODE)
+               CONF_DEBUG_ON(sr, SR);
+       TERM_DEBUG_ON(sr, SR);
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_sr,
+       no_debug_ospf_sr_cmd,
+       "no debug ospf sr",
+       NO_STR
+       DEBUG_STR
+       OSPF_STR
+       "OSPF-SR information\n")
+{
+       if (vty->node == CONFIG_NODE)
+               CONF_DEBUG_OFF(sr, SR);
+       TERM_DEBUG_OFF(sr, SR);
+       return CMD_SUCCESS;
+}
+
 DEFUN (no_debug_ospf,
        no_debug_ospf_cmd,
        "no debug ospf",
@@ -1758,6 +1788,18 @@ static int config_write_debug(struct vty *vty)
                write = 1;
        }
 
+       /* debug ospf te */
+       if (IS_CONF_DEBUG_OSPF(te, TE) == OSPF_DEBUG_TE) {
+               vty_out(vty, "debug ospf%s te\n", str);
+               write = 1;
+       }
+
+       /* debug ospf sr */
+       if (IS_CONF_DEBUG_OSPF(sr, SR) == OSPF_DEBUG_SR) {
+               vty_out(vty, "debug ospf%s sr\n", str);
+               write = 1;
+       }
+
        return write;
 }
 
@@ -1774,6 +1816,7 @@ void debug_init()
        install_element(ENABLE_NODE, &debug_ospf_event_cmd);
        install_element(ENABLE_NODE, &debug_ospf_nssa_cmd);
        install_element(ENABLE_NODE, &debug_ospf_te_cmd);
+       install_element(ENABLE_NODE, &debug_ospf_sr_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -1781,6 +1824,7 @@ void debug_init()
        install_element(ENABLE_NODE, &no_debug_ospf_event_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd);
        install_element(ENABLE_NODE, &no_debug_ospf_te_cmd);
+       install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd);
 
        install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd);
        install_element(ENABLE_NODE, &debug_ospf_packet_cmd);
@@ -1809,12 +1853,14 @@ void debug_init()
        install_element(CONFIG_NODE, &debug_ospf_event_cmd);
        install_element(CONFIG_NODE, &debug_ospf_nssa_cmd);
        install_element(CONFIG_NODE, &debug_ospf_te_cmd);
+       install_element(CONFIG_NODE, &debug_ospf_sr_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_event_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd);
        install_element(CONFIG_NODE, &no_debug_ospf_te_cmd);
+       install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd);
 
        install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd);
        install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd);
index ead2f526ba0ee5de0eee1a6017c7ed12376875d9..99d7512f16f19c30a44d6171d43085d230eb4930 100644 (file)
@@ -57,6 +57,8 @@
 #define OSPF_DEBUG_EVENT        0x01
 #define OSPF_DEBUG_NSSA                0x02
 #define OSPF_DEBUG_TE          0x04
+#define OSPF_DEBUG_EXT         0x08
+#define OSPF_DEBUG_SR          0x10
 
 /* Macro for setting debug option. */
 #define CONF_DEBUG_PACKET_ON(a, b)         conf_debug_ospf_packet[a] |= (b)
 /* Macro for checking debug option. */
 #define IS_DEBUG_OSPF_PACKET(a, b) (term_debug_ospf_packet[a] & OSPF_DEBUG_##b)
 #define IS_DEBUG_OSPF(a, b) (term_debug_ospf_##a & OSPF_DEBUG_##b)
-#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event,EVENT)
+#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event, EVENT)
 
-#define IS_DEBUG_OSPF_NSSA  IS_DEBUG_OSPF(nssa,NSSA)
+#define IS_DEBUG_OSPF_NSSA  IS_DEBUG_OSPF(nssa, NSSA)
 
-#define IS_DEBUG_OSPF_TE  IS_DEBUG_OSPF(te,TE)
+#define IS_DEBUG_OSPF_TE  IS_DEBUG_OSPF(te, TE)
+
+#define IS_DEBUG_OSPF_EXT  IS_DEBUG_OSPF(ext, EXT)
+
+#define IS_DEBUG_OSPF_SR  IS_DEBUG_OSPF(sr, SR)
 
 #define IS_CONF_DEBUG_OSPF_PACKET(a, b)                                        \
        (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
@@ -119,6 +125,8 @@ extern unsigned long term_debug_ospf_lsa;
 extern unsigned long term_debug_ospf_zebra;
 extern unsigned long term_debug_ospf_nssa;
 extern unsigned long term_debug_ospf_te;
+extern unsigned long term_debug_ospf_ext;
+extern unsigned long term_debug_ospf_sr;
 
 /* Message Strings. */
 extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c
new file mode 100644 (file)
index 0000000..d42476b
--- /dev/null
@@ -0,0 +1,1827 @@
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "network.h"
+#include "if.h"
+#include "libospf.h" /* for ospf interface types */
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ext.h"
+
+/* Following structure are internal use only. */
+
+/*
+ * Global variable to manage Extended Prefix/Link Opaque LSA on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_ext_lp OspfEXT;
+
+/*
+ * -----------------------------------------------------------------------
+ * Followings are initialize/terminate functions for Extended Prefix/Link
+ * Opaque LSA handling.
+ * -----------------------------------------------------------------------
+ */
+
+/* Extended Prefix Opaque LSA related callback functions */
+static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status);
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode);
+/* Extended Link Opaque LSA related callback functions */
+static int ospf_ext_link_new_if(struct interface *ifp);
+static int ospf_ext_link_del_if(struct interface *ifp);
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status);
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status);
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_link_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode);
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op);
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa);
+static void del_ext_info(void *val);
+
+/*
+ * Extended Link/Prefix initialization
+ *
+ * @param - none
+ *
+ * @return - 0 if OK, <> 0 otherwise
+ */
+int ospf_ext_init(void)
+{
+       int rc = 0;
+
+       memset(&OspfEXT, 0, sizeof(struct ospf_ext_lp));
+       OspfEXT.enabled = false;
+       /* Only Area flooding is supported yet */
+       OspfEXT.scope = OSPF_OPAQUE_AREA_LSA;
+       /* Initialize interface list */
+       OspfEXT.iflist = list_new();
+       OspfEXT.iflist->del = del_ext_info;
+
+       zlog_info("EXT (%s): Register Extended Link Opaque LSA", __func__);
+       rc = ospf_register_opaque_functab(
+               OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_EXTENDED_LINK_LSA,
+               ospf_ext_link_new_if,        /* new if */
+               ospf_ext_link_del_if,        /* del if */
+               ospf_ext_link_ism_change,    /* ism change */
+               ospf_ext_link_nsm_change,    /* nsm change */
+               NULL,                        /* Write router config. */
+               NULL,                        /* Write interface conf. */
+               NULL,                        /* Write debug config. */
+               ospf_ext_link_show_info,     /* Show LSA info */
+               ospf_ext_link_lsa_originate, /* Originate LSA */
+               ospf_ext_link_lsa_refresh,   /* Refresh LSA */
+               ospf_ext_link_lsa_update,    /* new_lsa_hook */
+               NULL);                       /* del_lsa_hook */
+
+       if (rc != 0) {
+               zlog_warn("EXT (%s): Failed to register Extended Link LSA",
+                         __func__);
+               return rc;
+       }
+
+       zlog_info("EXT (%s): Register Extended Prefix Opaque LSA", __func__);
+       rc = ospf_register_opaque_functab(
+               OspfEXT.scope, OPAQUE_TYPE_EXTENDED_PREFIX_LSA,
+               NULL,                        /* new if handle by link */
+               NULL,                        /* del if handle by link */
+               ospf_ext_pref_ism_change,    /* ism change */
+               NULL,                        /* nsm change */
+               ospf_sr_config_write_router, /* Write router config. */
+               NULL,                        /* Write interface conf. */
+               NULL,                        /* Write debug config. */
+               ospf_ext_pref_show_info,     /* Show LSA info */
+               ospf_ext_pref_lsa_originate, /* Originate LSA */
+               ospf_ext_pref_lsa_refresh,   /* Refresh LSA */
+               ospf_ext_pref_lsa_update,    /* new_lsa_hook */
+               NULL);                       /* del_lsa_hook */
+       if (rc != 0) {
+               zlog_warn("EXT (%s): Failed to register Extended Prefix LSA",
+                         __func__);
+               return rc;
+       }
+
+       return rc;
+}
+
+/*
+ * Extended Link/Prefix termination function
+ *
+ * @param - none
+ * @return - none
+ */
+void ospf_ext_term(void)
+{
+
+       if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
+           || (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
+               zlog_warn(
+                       "EXT: Unable to unregister Extended Prefix "
+                       "Opaque LSA functions: Wrong scope!");
+       else
+               ospf_delete_opaque_functab(OspfEXT.scope,
+                                          OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+
+       ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
+                                  OPAQUE_TYPE_EXTENDED_LINK_LSA);
+
+       list_delete_and_null(&OspfEXT.iflist);
+       OspfEXT.scope = 0;
+       OspfEXT.enabled = false;
+
+       return;
+}
+
+/*
+ * Extended Link/Prefix finish function
+ *
+ * @param - none
+ * @return - none
+ */
+void ospf_ext_finish(void)
+{
+       // list_delete_all_node(OspfEXT.iflist);
+       OspfEXT.enabled = false;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * Followings are control functions for Extended Prefix/Link Opaque LSA
+ * parameters management.
+ * ---------------------------------------------------------------------
+ */
+
+/* Functions to free memory space */
+static void del_ext_info(void *val)
+{
+       XFREE(MTYPE_OSPF_EXT_PARAMS, val);
+}
+
+/* Increment instance value for Extended Prefix Opaque LSAs Opaque ID field */
+static uint32_t get_ext_pref_instance_value(void)
+{
+       static uint32_t seqno = 0;
+
+       if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+               seqno += 1;
+       else
+               seqno = 1; /* Avoid zero. */
+
+       return seqno;
+}
+
+/* Increment instance value for Extended Link Opaque LSAs Opaque ID field */
+static uint32_t get_ext_link_instance_value(void)
+{
+       static uint32_t seqno = 0;
+
+       if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+               seqno += 1;
+       else
+               seqno = 1; /* Avoid zero. */
+
+       return seqno;
+}
+
+/* Lookup Extended Prefix/Links by ifp from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_ifp(struct interface *ifp)
+{
+       struct listnode *node, *nnode;
+       struct ext_itf *exti;
+
+       for (ALL_LIST_ELEMENTS(OspfEXT.iflist, node, nnode, exti))
+               if (exti->ifp == ifp)
+                       return exti;
+
+       return NULL;
+}
+
+/* Lookup Extended Prefix/Links by LSA ID from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_instance(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct ext_itf *exti;
+       uint32_t key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr));
+       uint8_t type = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+
+
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+               if ((exti->instance == key) && (exti->type == type))
+                       return exti;
+
+       return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * The underlying subsection defines setters and unsetters to create and
+ * delete tlvs and subtlvs
+ * ----------------------------------------------------------------------
+ */
+
+/* Extended Prefix TLV - RFC7684 section 2.1 */
+static void set_ext_prefix(struct ext_itf *exti, uint8_t route_type,
+                          uint8_t flags, struct prefix_ipv4 p)
+{
+
+       TLV_TYPE(exti->prefix) = htons(EXT_TLV_PREFIX);
+       /* Warning: Size must be adjust depending of subTLV's */
+       TLV_LEN(exti->prefix) = htons(EXT_TLV_PREFIX_SIZE);
+       exti->prefix.route_type = route_type;
+       exti->prefix.flags = flags;
+       /* Only Address Family Ipv4 (0) is defined in RFC 7684 */
+       exti->prefix.af = 0;
+       exti->prefix.pref_length = p.prefixlen;
+       exti->prefix.address = p.prefix;
+}
+
+/* Extended Link TLV - RFC7684 section 3.1 */
+static void set_ext_link(struct ext_itf *exti, uint8_t type, struct in_addr id,
+                        struct in_addr data)
+{
+
+       TLV_TYPE(exti->link) = htons(EXT_TLV_LINK);
+       /* Warning: Size must be adjust depending of subTLV's */
+       TLV_LEN(exti->link) = htons(EXT_TLV_LINK_SIZE);
+       exti->link.link_type = type;
+       exti->link.link_id = id;
+       exti->link.link_data = data;
+}
+
+/* Prefix SID SubTLV - section 5 */
+static void set_prefix_sid(struct ext_itf *exti, uint8_t algorithm,
+                          uint32_t value, int value_type, uint8_t flags)
+{
+
+       if ((algorithm != SR_ALGORITHM_SPF)
+           && (algorithm != SR_ALGORITHM_STRICT_SPF)) {
+               zlog_warn(
+                       "EXT (%s): unrecognized algorithm, not SPF or S-SPF",
+                       __func__);
+               return;
+       }
+
+       /* Update flags according to the type of value field: label or index */
+       if (value_type == SID_LABEL)
+               SET_FLAG(flags, EXT_SUBTLV_PREFIX_SID_VFLG);
+
+       /* set prefix sid subtlv for an extended prefix tlv */
+       TLV_TYPE(exti->node_sid) = htons(EXT_SUBTLV_PREFIX_SID);
+       exti->node_sid.algorithm = algorithm;
+       exti->node_sid.flags = flags;
+       exti->node_sid.mtid = 0; /* Multi-Topology is not supported */
+
+       /* Set Label or Index value */
+       if (value_type == SID_LABEL) {
+               TLV_LEN(exti->node_sid) = htons(SID_LABEL_SIZE);
+               exti->node_sid.value = htonl(SET_LABEL(value));
+       } else {
+               TLV_LEN(exti->node_sid) = htons(SID_INDEX_SIZE);
+               exti->node_sid.value = htonl(value);
+       }
+
+}
+
+/* Adjacency SID SubTLV - section 6.1 */
+static void set_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
+                       int value_type)
+{
+       int index;
+       uint8_t flags;
+
+       /* Determine which ADJ_SID must be set: nominal or backup */
+       if (backup) {
+               flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+               index = 1;
+       } else {
+               index = 0;
+               flags = 0;
+       }
+
+       /* Set Header */
+       TLV_TYPE(exti->adj_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+       /* Only Local ADJ-SID is supported for the moment */
+       SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+       exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */
+
+       /* Adjust Length, Flags and Value depending on the type of Label */
+       if (value_type == SID_LABEL) {
+               SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->adj_sid[index]) = htons(SID_LABEL_SIZE);
+               exti->adj_sid[index].value = htonl(SET_LABEL(value));
+       } else {
+               UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->adj_sid[index]) = htons(SID_INDEX_SIZE);
+               exti->adj_sid[index].value = htonl(value);
+       }
+
+       exti->adj_sid[index].flags = flags; /* Set computed flags */
+       exti->adj_sid[index].mtid = 0;   /* Multi-Topology is not supported */
+       exti->adj_sid[index].weight = 0; /* Load-Balancing is not supported */
+
+}
+
+/* LAN Adjacency SID SubTLV - section 6.2 */
+static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
+                           int value_type, struct in_addr neighbor_id)
+{
+
+       int index;
+       uint8_t flags;
+
+       /* Determine which ADJ_SID must be set: nominal or backup */
+       if (backup) {
+               flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+               index = 1;
+       } else {
+               index = 0;
+               flags = 0;
+       }
+
+       /* Set Header */
+       TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+       /* Only Local ADJ-SID is supported for the moment */
+       SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+       /* Adjust Length, Flags and Value depending on the type of Label */
+       if (value_type == SID_LABEL) {
+               SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->lan_sid[index]) = htons(SID_LABEL_SIZE);
+               exti->lan_sid[index].value = htonl(SET_LABEL(value));
+       } else {
+               UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+               TLV_LEN(exti->lan_sid[index]) = htons(SID_INDEX_SIZE);
+               exti->lan_sid[index].value = htonl(value);
+       }
+
+       exti->lan_sid[index].flags = flags; /* Set computed flags */
+       exti->lan_sid[index].mtid = 0;   /* Multi-Topology is not supported */
+       exti->lan_sid[index].weight = 0; /* Load-Balancing is not supported */
+       exti->lan_sid[index].neighbor_id = neighbor_id;
+
+}
+
+/* Experimental SubTLV from Cisco */
+static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif)
+{
+
+       TLV_TYPE(exti->rmt_itf_addr) = htons(EXT_SUBTLV_RMT_ITF_ADDR);
+       TLV_LEN(exti->rmt_itf_addr) = htons(sizeof(struct in_addr));
+       exti->rmt_itf_addr.value = rmtif;
+
+}
+
+/*
+ * Update Extended prefix SID index for Loopback interface type
+ *
+ * @param ifname - Loopback interface name
+ * @param index - new value for the prefix SID of this interface
+ * @param p - prefix for this interface or NULL if Extended Prefix
+ * should be remove
+ *
+ * @return instance number if update is OK, 0 otherwise
+ */
+uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
+                                       struct prefix_ipv4 *p, uint8_t flags)
+{
+       int rc = 0;
+       struct ext_itf *exti;
+
+       /* Find Extended Prefix interface */
+       exti = lookup_ext_by_ifp(ifp);
+       if (exti == NULL)
+               return rc;
+
+       if (p != NULL) {
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (%s): Schedule new prefix %s/%u with "
+                               "index %u on interface %s",
+                               __func__, inet_ntoa(p->prefix), p->prefixlen,
+                               index, ifp->name);
+
+               /* Set first Extended Prefix then the Prefix SID information */
+               set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
+                              *p);
+               set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX, flags);
+
+               /* Try to Schedule LSA */
+               SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                       ospf_ext_pref_lsa_schedule(exti, REFRESH_THIS_LSA);
+               else
+                       ospf_ext_pref_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+       } else {
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (%s): Remove prefix for interface %s",
+                               __func__, ifp->name);
+
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               }
+       }
+
+       return SET_OPAQUE_LSID(exti->type, exti->instance);
+}
+
+/*
+ * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding
+ *
+ * @param enable To activate or not Segment Routing Extended LSA flooding
+ *
+ * @return none
+ */
+void ospf_ext_update_sr(bool enable)
+{
+       struct listnode *node;
+       struct ext_itf *exti;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "EXT (%s): %s Extended LSAs for Segment Routing ",
+                       __func__, enable ? "Enable" : "Disable");
+
+       if (enable) {
+               OspfEXT.enabled = true;
+               /* Refresh LSAs if already engaged or originate */
+               for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+                       if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                               ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA);
+                       else
+                               ospf_ext_lsa_schedule(exti,
+                                                     REORIGINATE_THIS_LSA);
+       } else {
+               /* Start by Flushing engaged LSAs */
+               for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+                       if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                               ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
+               /* And then disable Extended Link/Prefix */
+               OspfEXT.enabled = false;
+       }
+}
+/*
+ * -----------------------------------------------------------------------
+ * Followings are callback functions against generic Opaque-LSAs handling
+ * -----------------------------------------------------------------------
+ */
+
+/* Add new Interface in Extended Interface List */
+static int ospf_ext_link_new_if(struct interface *ifp)
+{
+       struct ext_itf *new;
+       int rc = -1;
+
+       if (lookup_ext_by_ifp(ifp) != NULL) {
+               zlog_warn(
+                       "EXT (%s): interface %s is already in use",
+                       __func__, ifp ? ifp->name : "-");
+               rc = 0; /* Do nothing here. */
+               return rc;
+       }
+
+       new = XCALLOC(MTYPE_OSPF_EXT_PARAMS, sizeof(struct ext_itf));
+       if (new == NULL) {
+               zlog_warn("EXT (%s): XCALLOC: %s", __func__,
+                         safe_strerror(errno));
+               return rc;
+       }
+
+       /* initialize new information and link back the interface */
+       new->ifp = ifp;
+       new->flags = EXT_LPFLG_LSA_INACTIVE;
+
+       listnode_add(OspfEXT.iflist, new);
+
+       rc = 0;
+       return rc;
+}
+
+/* Remove existing Interface from Extended Interface List */
+static int ospf_ext_link_del_if(struct interface *ifp)
+{
+       struct ext_itf *exti;
+       int rc = -1;
+
+       exti = lookup_ext_by_ifp(ifp);
+       if (exti != NULL) {
+               struct list *iflist = OspfEXT.iflist;
+
+               /* Dequeue listnode entry from the list. */
+               listnode_delete(iflist, exti);
+
+               XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
+
+               rc = 0;
+       } else {
+               zlog_warn(
+                       "EXT (%s): interface %s is not found",
+                       __func__, ifp ? ifp->name : "-");
+       }
+
+       return rc;
+}
+
+/*
+ * Determine if an Interface belongs to an Extended Link Adjacency or LAN Adj.
+ * type and allocate new instance value accordingly
+ */
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status)
+{
+       struct ext_itf *exti;
+
+       /* Get interface information for Segment Routing */
+       exti = lookup_ext_by_ifp(oi->ifp);
+       if (exti == NULL) {
+               zlog_warn(
+                       "EXT (%s): Cannot get Extended info. from OI(%s)",
+                       __func__, IF_NAME(oi));
+               return;
+       }
+
+       /* Determine if interface is related to Adjacency or LAN Adj. SID */
+       if (oi->type != OSPF_IFTYPE_LOOPBACK) {
+               if (oi->state == ISM_DR)
+                       exti->stype = LAN_ADJ_SID;
+               else
+                       exti->stype = ADJ_SID;
+
+               exti->instance = get_ext_link_instance_value();
+               exti->type = OPAQUE_TYPE_EXTENDED_LINK_LSA;
+
+               zlog_debug(
+                       "EXT (%s): Set %s SID to interface %s ", __func__,
+                       exti->stype == ADJ_SID ? "Adj." : "LAN Adj.",
+                       oi->ifp->name);
+       }
+}
+
+/*
+ * Determine if an Interface belongs to an Extended Prefix and
+ * allocate new instance value accordingly
+ */
+static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status)
+{
+       struct ext_itf *exti;
+
+       /* Get interface information for Segment Routing */
+       exti = lookup_ext_by_ifp(oi->ifp);
+       if (exti == NULL) {
+               zlog_warn(
+                       "EXT (%s): Cannot get Extended info. from OI(%s)",
+                       __func__, IF_NAME(oi));
+               return;
+       }
+
+       /* Determine if interface is related to a Node SID */
+       if (oi->type == OSPF_IFTYPE_LOOPBACK) {
+               exti->stype = PREF_SID;
+               exti->instance = get_ext_pref_instance_value();
+               exti->type = OPAQUE_TYPE_EXTENDED_PREFIX_LSA;
+
+               zlog_debug(
+                       "EXT (%s): Set Node SID to interface %s ", __func__,
+                       oi->ifp->name);
+
+               /* Complete SRDB if the interface belongs to a Prefix */
+               if (OspfEXT.enabled)
+                       ospf_sr_update_prefix(oi->ifp, oi->address);
+       }
+}
+
+/*
+ * Finish Extended Link configuration and flood corresponding LSA
+ * when OSPF adjacency on this link fire up
+ */
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
+{
+       struct ospf_interface *oi = nbr->oi;
+       struct ext_itf *exti;
+       uint32_t label;
+
+       /* Process Neighbor only when its state is NSM Full */
+       if (nbr->state != NSM_Full)
+               return;
+
+       /* Get interface information for Segment Routing */
+       exti = lookup_ext_by_ifp(oi->ifp);
+       if (exti == NULL) {
+               zlog_warn(
+                       "EXT (%s): Cannot get Extended info. from OI(%s)",
+                       __func__, IF_NAME(oi));
+               return;
+       }
+
+       if (oi->area == NULL || oi->area->ospf == NULL) {
+               zlog_warn(
+                       "EXT (%s): Cannot refer to OSPF from OI(%s)",
+                       __func__, IF_NAME(oi));
+               return;
+       }
+
+       /* Keep Area information in combination with SR info. */
+       exti->area = oi->area;
+       OspfEXT.area = oi->area;
+
+       /* Process only Adjacency/LAN SID */
+       if (exti->stype == PREF_SID)
+               return;
+
+       switch (oi->state) {
+       case ISM_PointToPoint:
+               /* Segment ID is an Adjacency one */
+               exti->stype = ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == Nbr Router ID */
+               set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id,
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, true, label, SID_LABEL);
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, false, label, SID_LABEL);
+               /* And Remote Interface address */
+               set_rmt_itf_addr(exti, nbr->address.u.prefix4);
+
+               break;
+
+       case ISM_DR:
+               /* Segment ID is a LAN Adjacency for the DR only */
+               exti->stype = LAN_ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == DR */
+               set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_lan_adj_sid(exti, true, label, SID_LABEL, nbr->router_id);
+               label = get_ext_link_label_value();
+               set_lan_adj_sid(exti, false, label, SID_LABEL, nbr->router_id);
+
+               break;
+
+       case ISM_DROther:
+       case ISM_Backup:
+               /* Segment ID is an Adjacency if not the DR */
+               exti->stype = ADJ_SID;
+
+               /* Set Extended Link TLV with link_id == DR */
+               set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+                            oi->address->u.prefix4);
+
+               /* Set Extended Link Adjacency SubTLVs, backup first */
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, true, label, SID_LABEL);
+               label = get_ext_link_label_value();
+               set_adj_sid(exti, false, label, SID_LABEL);
+
+               break;
+
+       default:
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+               }
+               return;
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "EXT (%s): Complete %s SID to interface %s ", __func__,
+                       exti->stype == ADJ_SID ? "Adj." : "LAN Adj.",
+                       oi->ifp->name);
+
+       /* flood this links params if everything is ok */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+       if (OspfEXT.enabled) {
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+                       ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA);
+               else
+                       ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+       }
+
+}
+
+/* Callbacks to handle Extended Link Segment Routing LSA information */
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn("EXT (%s): Abort! LSA is NULL", __func__);
+               return -1;
+       }
+
+       /* Process only Opaque LSA */
+       if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+           && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+               return 0;
+
+       /* Process only Extended Link LSA */
+       if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+           != OPAQUE_TYPE_EXTENDED_LINK_LSA)
+               return 0;
+
+       /* Check if Extended is enable */
+       if (!OspfEXT.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ext_link_lsa_update(lsa);
+       else
+               ospf_sr_ext_link_lsa_delete(lsa);
+
+       return 0;
+}
+
+/* Callbacks to handle Extended Prefix Segment Routing LSA information */
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa)
+{
+
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn("EXT (%s): Abort! LSA is NULL", __func__);
+               return -1;
+       }
+
+       /* Process only Opaque LSA */
+       if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+           && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+               return 0;
+
+       /* Process only Extended Prefix LSA */
+       if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+           != OPAQUE_TYPE_EXTENDED_PREFIX_LSA)
+               return 0;
+
+       /* Check if it is not my LSA */
+       if (IS_LSA_SELF(lsa))
+               return 0;
+
+       /* Check if Extended is enable */
+       if (!OspfEXT.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ext_prefix_lsa_update(lsa);
+       else
+               ospf_sr_ext_prefix_lsa_delete(lsa);
+
+       return 0;
+}
+
+/*
+ * -------------------------------------------------------
+ * Followings are OSPF protocol processing functions for
+ * Extended Prefix/Link Opaque LSA
+ * -------------------------------------------------------
+ */
+
+static void build_tlv_header(struct stream *s, struct tlv_header *tlvh)
+{
+       stream_put(s, tlvh, sizeof(struct tlv_header));
+
+}
+
+static void build_tlv(struct stream *s, struct tlv_header *tlvh)
+{
+
+       if ((tlvh != NULL) && (ntohs(tlvh->type) != 0)) {
+               build_tlv_header(s, tlvh);
+               stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh));
+       }
+
+}
+
+/* Build an Extended Prefix Opaque LSA body for extended prefix TLV */
+static void ospf_ext_pref_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+
+       /* Sanity check */
+       if ((exti == NULL) || (exti->stype != PREF_SID))
+               return;
+
+       /* Adjust Extended Prefix TLV size */
+       TLV_LEN(exti->prefix) =
+               htons(ntohs(TLV_LEN(exti->node_sid)) + EXT_TLV_PREFIX_SIZE
+                           + TLV_HDR_SIZE);
+
+       /* Build LSA body for an Extended Prefix TLV */
+       build_tlv_header(s, &exti->prefix.header);
+       stream_put(s, TLV_DATA(&exti->prefix.header), EXT_TLV_PREFIX_SIZE);
+       /* Then add Prefix SID SubTLV */
+       build_tlv(s, &exti->node_sid.header);
+
+}
+
+/* Build an Extended Link Opaque LSA body for extended link TLV */
+static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+       size_t size;
+
+       /* Sanity check */
+       if ((exti == NULL)
+           || ((exti->stype != ADJ_SID) && (exti->stype != LAN_ADJ_SID)))
+               return;
+
+       if (exti->stype == ADJ_SID) {
+               /* Adjust Extended Link TLV size for Adj. SID */
+               size = EXT_TLV_LINK_SIZE + 2 * EXT_SUBTLV_ADJ_SID_SIZE
+                       + 2 * TLV_HDR_SIZE;
+               if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0)
+                       size = size + EXT_SUBTLV_RMT_ITF_ADDR_SIZE
+                                   + TLV_HDR_SIZE;
+               TLV_LEN(exti->link) = htons(size);
+
+               /* Build LSA body for an Extended Link TLV with Adj. SID */
+               build_tlv_header(s, &exti->link.header);
+               stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
+               /* then add Ajacency SubTLVs */
+               build_tlv(s, &exti->adj_sid[1].header);
+               build_tlv(s, &exti->adj_sid[0].header);
+
+               /* Add Cisco experimental SubTLV if interface is PtoP */
+               if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0)
+                       build_tlv(s, &exti->rmt_itf_addr.header);
+       } else {
+               /* Adjust Extended Link TLV size for LAN SID */
+               size = EXT_TLV_LINK_SIZE
+                       + 2 * (EXT_SUBTLV_LAN_ADJ_SID_SIZE + TLV_HDR_SIZE);
+               TLV_LEN(exti->link) = htons(size);
+
+               /* Build LSA body for an Extended Link TLV with LAN SID */
+               build_tlv_header(s, &exti->link.header);
+               stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE);
+               /* then add LAN-Ajacency SubTLVs */
+               build_tlv(s, &exti->lan_sid[1].header);
+               build_tlv(s, &exti->lan_sid[0].header);
+       }
+
+}
+
+/* Create new Extended Prefix opaque-LSA for every extended prefix */
+static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
+                                             struct ext_itf *exti)
+{
+       struct stream *s;
+       struct lsa_header *lsah;
+       struct ospf_lsa *new = NULL;
+       struct ospf *top;
+       u_char options, lsa_type;
+       struct in_addr lsa_id;
+       struct in_addr router_id;
+       uint32_t tmp;
+       uint16_t length;
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return NULL;
+
+       /* Create a stream for LSA. */
+       s = stream_new(OSPF_MAX_LSA_SIZE);
+       if (s == NULL) {
+               zlog_warn("EXT (%s): stream_new() error", __func__);
+               return NULL;
+       }
+
+       /* Prepare LSA Header */
+       lsah = (struct lsa_header *)STREAM_DATA(s);
+
+       lsa_type = OspfEXT.scope;
+
+       /*
+        * LSA ID is a variable number identifying different instances of
+        * Extended Prefix Opaque LSA from the same router see RFC 7684
+        */
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+       lsa_id.s_addr = htonl(tmp);
+
+       options = OSPF_OPTION_O; /* Don't forget this :-) */
+
+       /* Fix Options and Router ID depending of the flooding scope */
+       if ((OspfEXT.scope == OSPF_OPAQUE_AS_LSA) || (area == NULL)) {
+               options = OSPF_OPTION_E;
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+               router_id.s_addr = top ? top->router_id.s_addr : 0;
+       } else {
+               options |= LSA_OPTIONS_GET(area); /* Get area default option */
+               options |= LSA_OPTIONS_NSSA_GET(area);
+               router_id = area->ospf->router_id;
+       }
+
+       /* Set opaque-LSA header fields. */
+       lsa_header_set(s, options, lsa_type, lsa_id, router_id);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+               zlog_debug(
+                       "EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA "
+                       "Extended Prefix Opaque LSA instance",
+                       __func__, lsa_type, inet_ntoa(lsa_id));
+
+
+       /* Set opaque-LSA body fields. */
+       ospf_ext_pref_lsa_body_set(s, exti);
+
+       /* Set length. */
+       length = stream_get_endp(s);
+       lsah->length = htons(length);
+
+       /* Now, create an OSPF LSA instance. */
+       new = ospf_lsa_new();
+       if (new == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_new() error", __func__);
+               stream_free(s);
+               return NULL;
+       }
+       new->data = ospf_lsa_data_new(length);
+       if (new->data == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_data_new() error", __func__);
+               ospf_lsa_unlock(&new);
+               new = NULL;
+               stream_free(s);
+               return NULL;
+       }
+
+       /* Segment Routing belongs only to default VRF */
+       new->vrf_id = VRF_DEFAULT;
+       new->area = area;
+       SET_FLAG(new->flags, OSPF_LSA_SELF);
+       memcpy(new->data, lsah, length);
+       stream_free(s);
+
+       return new;
+}
+
+/* Create new Extended Link opaque-LSA for every extended link TLV */
+static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
+                                             struct ext_itf *exti)
+{
+       struct stream *s;
+       struct lsa_header *lsah;
+       struct ospf_lsa *new = NULL;
+       u_char options, lsa_type;
+       struct in_addr lsa_id;
+       uint32_t tmp;
+       uint16_t length;
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return NULL;
+
+       /* Create a stream for LSA. */
+       s = stream_new(OSPF_MAX_LSA_SIZE);
+       if (s == NULL) {
+               zlog_warn("EXT (%s): stream_new() error", __func__);
+               return NULL;
+       }
+       lsah = (struct lsa_header *)STREAM_DATA(s);
+
+       options = OSPF_OPTION_O;          /* Don't forget this :-) */
+       options |= LSA_OPTIONS_GET(area); /* Get area default option */
+       options |= LSA_OPTIONS_NSSA_GET(area);
+       /* Extended Link Opaque LSA are only flooded within an area */
+       lsa_type = OSPF_OPAQUE_AREA_LSA;
+
+       /*
+        * LSA ID is a variable number identifying different instances of
+        * Extended Link Opaque LSA from the same router see RFC 7684
+        */
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+       lsa_id.s_addr = htonl(tmp);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+               zlog_debug(
+                       "EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA "
+                       "Extended Link Opaque LSA instance",
+                       __func__, lsa_type, inet_ntoa(lsa_id));
+
+       /* Set opaque-LSA header fields. */
+       lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id);
+
+       /* Set opaque-LSA body fields. */
+       ospf_ext_link_lsa_body_set(s, exti);
+
+       /* Set length. */
+       length = stream_get_endp(s);
+       lsah->length = htons(length);
+
+       /* Now, create an OSPF LSA instance. */
+       new = ospf_lsa_new();
+       if (new == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_new() error", __func__);
+               stream_free(s);
+               return NULL;
+       }
+       new->data = ospf_lsa_data_new(length);
+       if (new->data == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_data_new() error", __func__);
+               ospf_lsa_unlock(&new);
+               new = NULL;
+               stream_free(s);
+               return NULL;
+       }
+
+       /* Segment Routing belongs only to default VRF */
+       new->vrf_id = VRF_DEFAULT;
+       new->area = area;
+       SET_FLAG(new->flags, OSPF_LSA_SELF);
+       memcpy(new->data, lsah, length);
+       stream_free(s);
+
+       return new;
+}
+
+/*
+ * Process the origination of an Extended Prefix Opaque LSA
+ * for every extended prefix TLV
+ */
+static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
+                                       struct ext_itf *exti)
+{
+       struct ospf_lsa *new;
+       int rc = -1;
+
+
+       /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+       new = ospf_ext_pref_lsa_new(area, exti);
+       if (new == NULL) {
+               zlog_warn("EXT (%s): ospf_ext_pref_lsa_new() error", __func__);
+               return rc;
+       }
+
+       /* Install this LSA into LSDB. */
+       if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+               ospf_lsa_unlock(&new);
+               return rc;
+       }
+
+       /* Now this Extended Prefix Opaque LSA info parameter entry has
+        * associated LSA.
+        */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+       /* Update new LSA origination count. */
+       area->ospf->lsa_originate_count++;
+
+       /* Flood new LSA through area. */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               char area_id[INET_ADDRSTRLEN];
+
+               strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+               zlog_debug(
+                       "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
+                       "Extended Prefix Opaque LSA: Area(%s), Link(%s)",
+                       __func__, new->data->type, inet_ntoa(new->data->id),
+                       area_id, exti->ifp->name);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       rc = 0;
+
+       return rc;
+}
+
+/*
+ * Process the origination of an Extended Link Opaque LSA
+ * for every extended link TLV
+ */
+static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
+                                       struct ext_itf *exti)
+{
+       struct ospf_lsa *new;
+       int rc = -1;
+
+       /* Create new Opaque-LSA/Extended Link Opaque LSA instance. */
+       new = ospf_ext_link_lsa_new(area, exti);
+       if (new == NULL) {
+               zlog_warn("EXT (%s): ospf_ext_link_lsa_new() error", __func__);
+               return rc;
+       }
+
+       /* Install this LSA into LSDB. */
+       if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+               ospf_lsa_unlock(&new);
+               return rc;
+       }
+
+       /* Now this link-parameter entry has associated LSA. */
+       SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+       /* Update new LSA origination count. */
+       area->ospf->lsa_originate_count++;
+
+       /* Flood new LSA through area. */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               char area_id[INET_ADDRSTRLEN];
+
+               strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+               zlog_debug(
+                       "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
+                       "Extended Link Opaque LSA: Area(%s), Link(%s)",
+                        __func__, new->data->type, inet_ntoa(new->data->id),
+                        area_id, exti->ifp->name);
+               ospf_lsa_header_dump(new->data);
+       }
+
+       rc = 0;
+
+       return rc;
+}
+
+/* Trigger the origination of Extended Prefix Opaque LSAs */
+static int ospf_ext_pref_lsa_originate(void *arg)
+{
+       struct ospf_area *area = (struct ospf_area *)arg;
+       struct listnode *node;
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if (!OspfEXT.enabled) {
+               zlog_info(
+                       "EXT (%s): Segment Routing "
+                       "functionality is Disabled now", __func__);
+               rc = 0; /* This is not an error case. */
+               return rc;
+       }
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "EXT (%s): Start Originate Prefix LSA for area %s",
+                       __func__, inet_ntoa(area->area_id));
+
+       /* Check if Extended Prefix Opaque LSA is already engaged */
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+
+               /* Process only Prefix SID */
+               if (exti->stype != PREF_SID)
+                       continue;
+
+               /* Process only Extended Prefix with valid Area ID */
+               if ((exti->area == NULL)
+                   || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+                       continue;
+
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       if (CHECK_FLAG(exti->flags,
+                                      EXT_LPFLG_LSA_FORCED_REFRESH)) {
+                               zlog_warn(
+                                       "EXT (%s): Refresh instead of "
+                                       "Originate", __func__);
+                               UNSET_FLAG(exti->flags,
+                                          EXT_LPFLG_LSA_FORCED_REFRESH);
+                               ospf_ext_pref_lsa_schedule(exti,
+                                                          REFRESH_THIS_LSA);
+                       }
+                       continue;
+               }
+
+               /* Ok, let's try to originate an LSA */
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (%s): Let's finally reoriginate the "
+                               "LSA 7.0.0.%u for Itf %s",
+                               __func__, exti->instance,
+                               exti->ifp ? exti->ifp->name : "");
+               ospf_ext_pref_lsa_originate1(area, exti);
+       }
+
+       rc = 0;
+       return rc;
+}
+
+/* Trigger the origination of Extended Link Opaque LSAs */
+static int ospf_ext_link_lsa_originate(void *arg)
+{
+       struct ospf_area *area = (struct ospf_area *)arg;
+       struct listnode *node;
+       struct ext_itf *exti;
+       int rc = -1;
+
+       if (!OspfEXT.enabled) {
+               zlog_info(
+                       "EXT (%s): Segment Routing "
+                       "functionality is Disabled now", __func__);
+               rc = 0; /* This is not an error case. */
+               return rc;
+       }
+
+       /* Check if Extended Prefix Opaque LSA is already engaged */
+       for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+               /* Process only Adjacency or LAN SID */
+               if (exti->stype == PREF_SID)
+                       continue;
+
+               /* Process only Extended Link with valid Area ID */
+               if ((exti->area == NULL)
+                   || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+                       continue;
+
+               /* Check if LSA not already engaged */
+               if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+                       if (CHECK_FLAG(exti->flags,
+                                      EXT_LPFLG_LSA_FORCED_REFRESH)) {
+                               zlog_warn(
+                                       "EXT (%s): Refresh instead of "
+                                       "Originate", __func__);
+                               UNSET_FLAG(exti->flags,
+                                          EXT_LPFLG_LSA_FORCED_REFRESH);
+                               ospf_ext_link_lsa_schedule(exti,
+                                                          REFRESH_THIS_LSA);
+                       }
+                       continue;
+               }
+
+               /* Ok, let's try to originate an LSA */
+               if (IS_DEBUG_OSPF_SR)
+                       zlog_debug(
+                               "EXT (%s): Let's finally reoriginate the "
+                               "LSA 8.0.0.%u for Itf %s through the Area %s",
+                               __func__, exti->instance,
+                               exti->ifp ? exti->ifp->name : "-",
+                               inet_ntoa(area->area_id));
+               ospf_ext_link_lsa_originate1(area, exti);
+       }
+
+       rc = 0;
+       return rc;
+}
+
+/* Refresh an Extended Prefix Opaque LSA */
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
+{
+       struct ospf_lsa *new = NULL;
+       struct ospf_area *area = lsa->area;
+       struct ospf *top;
+       struct ext_itf *exti;
+
+       if (!OspfEXT.enabled) {
+               /*
+                * This LSA must have flushed before due to Extended Prefix
+                * Opaque LSA status change.
+                * It seems a slip among routers in the routing domain.
+                */
+               zlog_info(
+                       "EXT (%s): Segment Routing functionality is "
+                       "Disabled", __func__);
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Lookup this lsa corresponding Extended parameters */
+       exti = lookup_ext_by_instance(lsa);
+       if (exti == NULL) {
+               zlog_warn("EXT (%s): Invalid parameter LSA ID", __func__);
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Check if Interface was not disable in the interval */
+       if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+               zlog_warn("EXT (%s): Interface was Disabled: Flush it!",
+                       __func__);
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* If the lsa's age reached to MaxAge, start flushing procedure. */
+       if (IS_LSA_MAXAGE(lsa)) {
+               if (exti)
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(lsa);
+               return NULL;
+       }
+
+       /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+       new = ospf_ext_pref_lsa_new(area, exti);
+
+       if (new == NULL) {
+               zlog_warn("EXT (%s): ospf_ext_pref_lsa_new() error", __func__);
+               return NULL;
+       }
+       new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+       /*
+        * Install this LSA into LSDB
+        * Given "lsa" will be freed in the next function
+        * As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use
+        * ospf_lookup() to get ospf instance
+        */
+       if (area)
+               top = area->ospf;
+       else
+               top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+
+       if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+               ospf_lsa_unlock(&new);
+               return NULL;
+       }
+
+       /* Flood updated LSA through the Prefix Area according to the RFC7684 */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug(
+                       "EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA",
+                       __func__, new->data->type, inet_ntoa(new->data->id));
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
+/* Refresh an Extended Link Opaque LSA */
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa)
+{
+       struct ext_itf *exti;
+       struct ospf_area *area = lsa->area;
+       struct ospf *top = area->ospf;
+       struct ospf_lsa *new = NULL;
+
+       if (!OspfEXT.enabled) {
+               /*
+                * This LSA must have flushed before due to OSPF-SR status
+                * change. It seems a slip among routers in the routing domain.
+                */
+               zlog_info(
+                       "EXT (%s): Segment Routing functionality is Disabled",
+                       __func__);
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Lookup this LSA corresponding Extended parameters */
+       exti = lookup_ext_by_instance(lsa);
+       if (exti == NULL) {
+               zlog_warn("EXT (%s): Invalid parameter LSA ID", __func__);
+               /* Flush it anyway. */
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* Check if Interface was not disable in the interval */
+       if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+               zlog_warn(
+                       "EXT (%s): Interface was Disabled: Flush it!",
+                       __func__);
+               lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+       }
+
+       /* If the lsa's age reached to MaxAge, start flushing procedure */
+       if (IS_LSA_MAXAGE(lsa)) {
+               if (exti)
+                       UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(lsa);
+               return NULL;
+       }
+
+       /* Create new Opaque-LSA/Extended Link instance */
+       new = ospf_ext_link_lsa_new(area, exti);
+       if (new == NULL) {
+               zlog_warn("EXT (%s): Error creating new LSA", __func__);
+               return NULL;
+       }
+       new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+       /* Install this LSA into LSDB. */
+       /* Given "lsa" will be freed in the next function */
+       if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+               zlog_warn("EXT (%s): Error installing new LSA", __func__);
+               ospf_lsa_unlock(&new);
+               return NULL;
+       }
+
+       /* Flood updated LSA through the link Area according to the RFC7684 */
+       ospf_flood_through_area(area, NULL /*nbr */, new);
+
+       /* Debug logging. */
+       if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+               zlog_debug(
+                       "EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA",
+                       __func__, new->data->type, inet_ntoa(new->data->id));
+               ospf_lsa_header_dump(new->data);
+       }
+
+       return new;
+}
+
+/* Schedule Extended Prefix Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode)
+{
+       struct ospf_lsa lsa;
+       struct lsa_header lsah;
+       struct ospf *top;
+       uint32_t tmp;
+
+       memset(&lsa, 0, sizeof(lsa));
+       memset(&lsah, 0, sizeof(lsah));
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return;
+
+       /* Check if the corresponding link is ready to be flooded */
+       if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+               return;
+
+       zlog_debug(
+               "EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+               opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+               opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+               opcode == FLUSH_THIS_LSA ? "Flush" : "",
+               exti->ifp ? exti->ifp->name : "-");
+
+       /* Set LSA header information */
+       if (exti->area == NULL) {
+               zlog_warn(
+                       "EXT (%s): Flooding is Area scope but area is not yet "
+                       "set", __func__);
+               if (OspfEXT.area == NULL) {
+                       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+                       OspfEXT.area = ospf_area_lookup_by_area_id(
+                               top, OspfEXT.area_id);
+               }
+               exti->area = OspfEXT.area;
+       }
+       lsa.area = exti->area;
+       lsa.data = &lsah;
+       lsah.type = OSPF_OPAQUE_AREA_LSA;
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+       lsah.id.s_addr = htonl(tmp);
+
+       switch (opcode) {
+       case REORIGINATE_THIS_LSA:
+               ospf_opaque_lsa_reoriginate_schedule(
+                       (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+                       OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+               break;
+       case REFRESH_THIS_LSA:
+               ospf_opaque_lsa_refresh_schedule(&lsa);
+               break;
+       case FLUSH_THIS_LSA:
+               UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(&lsa);
+               break;
+       default:
+               zlog_warn("EXT (%s): Unknown opcode", __func__);
+               break;
+       }
+
+}
+
+/* Schedule Extended Link Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+                                      enum lsa_opcode opcode)
+{
+       struct ospf_lsa lsa;
+       struct lsa_header lsah;
+       struct ospf *top;
+       uint32_t tmp;
+
+       memset(&lsa, 0, sizeof(lsa));
+       memset(&lsah, 0, sizeof(lsah));
+
+       /* Sanity Check */
+       if (exti == NULL)
+               return;
+
+       /* Check if the corresponding link is ready to be flooded */
+       if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+               return;
+
+       zlog_debug(
+               "EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+               opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+               opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+               opcode == FLUSH_THIS_LSA ? "Flush" : "",
+               exti->ifp ? exti->ifp->name : "-");
+
+       /* Set LSA header information */
+       if (exti->area == NULL) {
+               zlog_warn(
+                       "EXT (%s): Flooding is Area scope but area is not "
+                       "yet set", __func__);
+               if (OspfEXT.area == NULL) {
+                       top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+                       OspfEXT.area = ospf_area_lookup_by_area_id(
+                               top, OspfEXT.area_id);
+               }
+               exti->area = OspfEXT.area;
+       }
+       lsa.area = exti->area;
+       lsa.data = &lsah;
+       lsah.type = OSPF_OPAQUE_AREA_LSA;
+       tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+       lsah.id.s_addr = htonl(tmp);
+
+       switch (opcode) {
+       case REORIGINATE_THIS_LSA:
+               ospf_opaque_lsa_reoriginate_schedule(
+                       (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+                       OPAQUE_TYPE_EXTENDED_LINK_LSA);
+               break;
+       case REFRESH_THIS_LSA:
+               ospf_opaque_lsa_refresh_schedule(&lsa);
+               break;
+       case FLUSH_THIS_LSA:
+               UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+               ospf_opaque_lsa_flush_schedule(&lsa);
+               break;
+       default:
+               zlog_warn("EXT (%s): Unknown opcode", __func__);
+               break;
+       }
+
+}
+
+/* Schedule Extended Link or Prefix depending of the Type of LSA */
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op)
+{
+
+       if (exti->stype == PREF_SID)
+               ospf_ext_pref_lsa_schedule(exti, op);
+       else
+               ospf_ext_link_lsa_schedule(exti, op);
+}
+
+/*
+ * ------------------------------------
+ * Followings are vty show functions.
+ * ------------------------------------
+ */
+
+/* Cisco experimental SubTLV */
+static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty,
+                                               struct tlv_header *tlvh)
+{
+       struct ext_subtlv_rmt_itf_addr *top;
+
+       top = (struct ext_subtlv_rmt_itf_addr *)tlvh;
+
+       vty_out(vty,
+               "  Remote Interface Address Sub-TLV: Length %u\n        "
+               "Address: %s\n",
+               ntohs(top->header.length), inet_ntoa(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Adjacency SID SubTLV */
+static uint16_t show_vty_ext_link_adj_sid(struct vty *vty,
+                                          struct tlv_header *tlvh)
+{
+       struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh;
+
+       vty_out(vty,
+               "  Adj-SID Sub-TLV: Length %u\n\tFlags: "
+               "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n",
+               ntohs(top->header.length), top->flags, top->mtid, top->weight,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+                                                                    : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* LAN Adjacency SubTLV */
+static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty,
+                                              struct tlv_header *tlvh)
+{
+       struct ext_subtlv_lan_adj_sid *top =
+               (struct ext_subtlv_lan_adj_sid *)tlvh;
+
+       vty_out(vty,
+               "  LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
+               "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
+               "%s\n\tLabel: %u\n",
+               ntohs(top->header.length), top->flags, top->mtid, top->weight,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+                                                                    : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh)
+{
+       vty_out(vty, "    Unknown TLV: [type(0x%x), length(0x%x)]\n",
+               ntohs(tlvh->type), ntohs(tlvh->length));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Extended Link Sub TLVs */
+static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext)
+{
+       struct ext_tlv_link *top = (struct ext_tlv_link *)ext;
+       struct tlv_header *tlvh;
+       uint16_t length = ntohs(top->header.length) - 3 * sizeof(uint32_t);
+       uint16_t sum = 0;
+
+       vty_out(vty,
+               "  Extended Link TLV: Length %u\n       Link Type: 0x%x\n"
+               "       Link ID: %s\n",
+               ntohs(top->header.length), top->link_type,
+               inet_ntoa(top->link_id));
+       vty_out(vty, "  Link data: %s\n", inet_ntoa(top->link_data));
+
+       tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+                                    + EXT_TLV_LINK_SIZE);
+       for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_SUBTLV_ADJ_SID:
+                       sum += show_vty_ext_link_adj_sid(vty, tlvh);
+                       break;
+               case EXT_SUBTLV_LAN_ADJ_SID:
+                       sum += show_vty_ext_link_lan_adj_sid(vty, tlvh);
+                       break;
+               case EXT_SUBTLV_RMT_ITF_ADDR:
+                       sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return sum + sizeof(struct ext_tlv_link);
+}
+
+/* Extended Link TLVs */
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct tlv_header *tlvh;
+       uint16_t length = 0, sum = 0;
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_TLV_LINK:
+                       sum += show_vty_link_info(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+}
+
+/* Prefix SID SubTLV */
+static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty,
+                                           struct tlv_header *tlvh)
+{
+       struct ext_subtlv_prefix_sid *top =
+               (struct ext_subtlv_prefix_sid *)tlvh;
+
+       vty_out(vty,
+               "  Prefix SID Sub-TLV: Length %u\n\tAlgorithm: "
+               "%u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n",
+               ntohs(top->header.length), top->algorithm, top->flags,
+               top->mtid,
+               CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label"
+                                                                  : "Index",
+               CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG)
+                       ? GET_LABEL(ntohl(top->value))
+                       : ntohl(top->value));
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Extended Prefix SubTLVs */
+static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext)
+{
+       struct ext_tlv_prefix *top = (struct ext_tlv_prefix *)ext;
+       struct tlv_header *tlvh;
+       uint16_t length = ntohs(top->header.length) - 2 * sizeof(uint32_t);
+       uint16_t sum = 0;
+
+       vty_out(vty,
+               "  Extended Prefix TLV: Length %u\n\tRoute Type: %u\n"
+               "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %s/%u\n",
+               ntohs(top->header.length), top->route_type, top->af, top->flags,
+               inet_ntoa(top->address), top->pref_length);
+
+       tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+                                    + EXT_TLV_PREFIX_SIZE);
+       for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_SUBTLV_PREFIX_SID:
+                       sum += show_vty_ext_pref_pref_sid(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+       return sum + sizeof(struct ext_tlv_prefix);
+}
+
+/* Extended Prefix TLVs */
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct tlv_header *tlvh;
+       uint16_t length = 0, sum = 0;
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case EXT_TLV_PREFIX:
+                       sum += show_vty_pref_info(vty, tlvh);
+                       break;
+               default:
+                       sum += show_vty_unknown_tlv(vty, tlvh);
+                       break;
+               }
+       }
+
+}
diff --git a/ospfd/ospf_ext.h b/ospfd/ospf_ext.h
new file mode 100644 (file)
index 0000000..6728075
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA header definition
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_OSPF_EXT_PREF_H_
+#define _FRR_OSPF_EXT_PREF_H_
+
+/*
+ * Opaque LSA's link state ID for Extended Prefix/Link is
+ * structured as follows.
+ *
+ *        24       16        8        0
+ * +--------+--------+--------+--------+
+ * |  7/8   |........|........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<------- Instance ------->|
+ *
+ *
+ * Type:      IANA has assigned '7' for Extended Prefix Opaque LSA
+ *            and '8' for Extended Link Opaque LSA
+ * Instance:  User may select arbitrary 24-bit values to identify
+ *            different instances of Extended Prefix/Link Opaque LSA
+ *
+ */
+
+/*
+ *        24       16        8        0
+ * +--------+--------+--------+--------+ ---
+ * |   LS age        |Options |  10,11 |  A
+ * +--------+--------+--------+--------+  |  Standard (Opaque) LSA header;
+ * |   7/8  |        Instance          |  |
+ * +--------+--------+--------+--------+  |  Type 10 or 11 are used for Extended
+ * |        Advertising router         |  |  Prefix Opaque LSA
+ * +--------+--------+--------+--------+  |
+ * |        LS sequence number         |  |  Type 10 only is used for Extended
+ * +--------+--------+--------+--------+  |  Link Opaque LSA
+ * |   LS checksum   |     Length      |  V
+ * +--------+--------+--------+--------+ ---
+ * |      Type       |     Length      |  A
+ * +--------+--------+--------+--------+  |  TLV part for Extended Prefix/Link
+ * |                                   |  |  Opaque LSA;
+ * ~              Values ...           ~  |  Values might be structured as a set
+ * |                                   |  V  of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/* Global use constant numbers */
+
+#define MAX_LEGAL_EXT_INSTANCE_NUM     (0xffff)
+#define LEGAL_EXT_INSTANCE_RANGE(i)    (0 <= (i) && (i) <= 0xffff)
+
+/* Flags to manage Extended Link/Prefix Opaque LSA */
+#define EXT_LPFLG_LSA_INACTIVE          0x00
+#define EXT_LPFLG_LSA_ACTIVE            0x01
+#define EXT_LPFLG_LSA_ENGAGED           0x02
+#define EXT_LPFLG_LSA_LOOKUP_DONE       0x04
+#define EXT_LPFLG_LSA_FORCED_REFRESH    0x08
+#define EXT_LPFLG_FIB_ENTRY_SET         0x10
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Extended Prefix TLV Route Types */
+#define EXT_TLV_PREF_ROUTE_UNSPEC      0
+#define EXT_TLV_PREF_ROUTE_INTRA_AREA  1
+#define EXT_TLV_PREF_ROUTE_INTER_AREA  3
+#define EXT_TLV_PREF_ROUTE_AS_EXT      5
+#define EXT_TLV_PREF_ROUTE_NSSA_EXT    7
+
+/*
+ * Extended Prefix and Extended Prefix Range TLVs'
+ * Address family flag for IPv4
+ */
+#define EXT_TLV_PREF_AF_IPV4           0
+
+/* Extended Prefix TLV Flags */
+#define EXT_TLV_PREF_AFLG              0x80
+#define EXT_TLV_PREF_NFLG              0x40
+
+/* Extended Prefix Range TLV Flags */
+#define EXT_TLV_PREF_RANGE_IAFLG       0x80
+
+/* ERO subtlvs Flags */
+#define EXT_SUBTLV_ERO_LFLG            0x80
+
+/* Extended Prefix TLV see RFC 7684 section 2.1 */
+#define EXT_TLV_PREFIX                 1
+#define EXT_TLV_PREFIX_SIZE            8
+struct ext_tlv_prefix {
+       struct tlv_header header;
+       uint8_t route_type;
+       uint8_t pref_length;
+       uint8_t af;
+       uint8_t flags;
+       struct in_addr address;
+};
+
+/* Extended Link TLV see RFC 7684 section 3.1 */
+#define EXT_TLV_LINK                   1
+#define EXT_TLV_LINK_SIZE              12
+struct ext_tlv_link {
+       struct tlv_header header;
+       uint8_t link_type;
+       uint8_t reserved[3];
+       struct in_addr link_id;
+       struct in_addr link_data;
+};
+
+/* Remote Interface Address Sub-TLV, Cisco experimental use Sub-TLV */
+#define EXT_SUBTLV_RMT_ITF_ADDR         32768
+#define EXT_SUBTLV_RMT_ITF_ADDR_SIZE   4
+struct ext_subtlv_rmt_itf_addr {
+       struct tlv_header header;
+       struct in_addr value;
+};
+
+/* Internal structure to manage Extended Link/Prefix Opaque LSA */
+struct ospf_ext_lp {
+       bool enabled;
+
+       /* Flags to manage this Extended Prefix/Link Opaque LSA */
+       uint32_t flags;
+
+       /*
+        * Scope is area Opaque Type 10 or AS Opaque LSA Type 11 for
+        * Extended Prefix and area Opaque Type 10 for Extended Link
+        */
+       uint8_t scope;
+
+       /* area pointer if flooding is Type 10 Null if flooding is AS scope */
+       struct ospf_area *area;
+       struct in_addr area_id;
+
+       /* List of interface with Segment Routing enable */
+       struct list *iflist;
+};
+
+/* Structure to aggregate interfaces information for Extended Prefix/Link */
+struct ext_itf {
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       uint32_t instance;
+       uint8_t type; /* Extended Prefix (7) or Link (8) */
+
+       /* Reference pointer to a Zebra-interface. */
+       struct interface *ifp;
+
+       /* Area info in which this SR link belongs to. */
+       struct ospf_area *area;
+
+       /* Flags to manage this link parameters. */
+       uint32_t flags;
+
+       /* SID type: Node, Adjacency or LAN Adjacency */
+       enum sid_type stype;
+
+       /* extended link/prefix TLV information */
+       struct ext_tlv_prefix prefix;
+       struct ext_subtlv_prefix_sid node_sid;
+       struct ext_tlv_link link;
+       struct ext_subtlv_adj_sid adj_sid[2];
+       struct ext_subtlv_lan_adj_sid lan_sid[2];
+
+       /* cisco experimental subtlv */
+       struct ext_subtlv_rmt_itf_addr rmt_itf_addr;
+};
+
+/* Prototypes. */
+extern int ospf_ext_init(void);
+extern void ospf_ext_term(void);
+extern void ospf_ext_finish(void);
+extern void ospf_ext_update_sr(bool enable);
+extern uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp,
+                                         uint32_t index,
+                                         struct prefix_ipv4 *p,
+                                         uint8_t flags);
+#endif /* _FRR_OSPF_EXT_PREF_H_ */
index e8700e7eb062611385170dd92bd40582912a52d2..c8f758525ed07a66378d4bb3141a4bd6c54f7996 100644 (file)
@@ -51,6 +51,28 @@ DEFINE_QOBJ_TYPE(ospf_interface)
 DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd))
 DEFINE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd))
 
+int ospf_interface_neighbor_count(struct ospf_interface *oi)
+{
+       int count = 0;
+       struct route_node *rn;
+       struct ospf_neighbor *nbr = NULL;
+
+       for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+               nbr = rn->info;
+               if (nbr) {
+                       /* Do not show myself. */
+                       if (nbr == oi->nbr_self)
+                               continue;
+                       /* Down state is not shown. */
+                       if (nbr->state == NSM_Down)
+                               continue;
+                       count++;
+               }
+       }
+
+       return count;
+}
+
 int ospf_if_get_output_cost(struct ospf_interface *oi)
 {
        /* If all else fails, use default OSPF cost */
index 829a3f4297985f172b5c14448a7742c23df50f83..ab02444f7d82228f5e694af46391929e7986a3c1 100644 (file)
@@ -314,8 +314,8 @@ extern struct crypt_key *ospf_crypt_key_lookup(struct list *, u_char);
 extern struct crypt_key *ospf_crypt_key_new(void);
 extern void ospf_crypt_key_add(struct list *, struct crypt_key *);
 extern int ospf_crypt_key_delete(struct list *, u_char);
-
 extern u_char ospf_default_iftype(struct interface *ifp);
+extern int ospf_interface_neighbor_count(struct ospf_interface *oi);
 
 /* Set all multicast memberships appropriately based on the type and
    state of the interface. */
index cdc9b929fa1f3b3f179b82a842b532faac6a2b8c..1332104b0afdd6a384f2b1cf6ff14a4785107d7c 100644 (file)
@@ -53,3 +53,5 @@ DEFINE_MTYPE(OSPFD, OSPF_IF_PARAMS, "OSPF if params")
 DEFINE_MTYPE(OSPFD, OSPF_MESSAGE, "OSPF message")
 DEFINE_MTYPE(OSPFD, OSPF_MPLS_TE, "OSPF MPLS parameters")
 DEFINE_MTYPE(OSPFD, OSPF_PCE_PARAMS, "OSPF PCE parameters")
+DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters")
+DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters")
index 5f5960eec7cc48c01ffdd9ba047a5f587bd24335..50c6f33ecfbc4c5d03ab4747d9c8c5461f96573b 100644 (file)
@@ -52,5 +52,7 @@ DECLARE_MTYPE(OSPF_IF_PARAMS)
 DECLARE_MTYPE(OSPF_MESSAGE)
 DECLARE_MTYPE(OSPF_MPLS_TE)
 DECLARE_MTYPE(OSPF_PCE_PARAMS)
+DECLARE_MTYPE(OSPF_SR_PARAMS)
+DECLARE_MTYPE(OSPF_EXT_PARAMS)
 
 #endif /* _QUAGGA_OSPF_MEMORY_H */
index 6f9da925428973e3e238f47e976bdb690e42230b..1c586d252cb752ff95880f97c28e1cc5df1a9a0e 100644 (file)
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
 
 DEFINE_MTYPE_STATIC(OSPFD, OSPF_OPAQUE_FUNCTAB, "OSPF opaque function table")
 DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_TYPE, "OSPF opaque per-type info")
@@ -59,9 +63,6 @@ DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_ID, "OSPF opaque per-ID info")
  * Followings are initialize/terminate functions for Opaque-LSAs handling.
  *------------------------------------------------------------------------*/
 
-#include "ospfd/ospf_te.h"
-#include "ospfd/ospf_ri.h"
-
 #ifdef SUPPORT_OSPF_API
 int ospf_apiserver_init(void);
 void ospf_apiserver_term(void);
@@ -85,9 +86,16 @@ void ospf_opaque_init(void)
        if (ospf_mpls_te_init() != 0)
                exit(1);
 
+       /* Segment Routing init */
+       if (ospf_sr_init() != 0)
+               exit(1);
+
        if (ospf_router_info_init() != 0)
                exit(1);
 
+       if (ospf_ext_init() != 0)
+               exit(1);
+
 #ifdef SUPPORT_OSPF_API
        if ((ospf_apiserver_enable) && (ospf_apiserver_init() != 0))
                exit(1);
@@ -102,6 +110,10 @@ void ospf_opaque_term(void)
 
        ospf_router_info_term();
 
+       ospf_ext_term();
+
+       ospf_sr_term();
+
 #ifdef SUPPORT_OSPF_API
        ospf_apiserver_term();
 #endif /* SUPPORT_OSPF_API */
@@ -110,6 +122,15 @@ void ospf_opaque_term(void)
        return;
 }
 
+void ospf_opaque_finish(void)
+{
+       ospf_router_info_finish();
+
+       ospf_ext_finish();
+
+       ospf_sr_finish();
+}
+
 int ospf_opaque_type9_lsa_init(struct ospf_interface *oi)
 {
        if (oi->opaque_lsa_self != NULL)
@@ -209,6 +230,12 @@ static const char *ospf_opaque_type_name(u_char opaque_type)
        case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
                name = "Router Information LSA";
                break;
+       case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+               name = "Extended Prefix Opaque LSA";
+               break;
+       case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+               name = "Extended Link Opaque LSA";
+               break;
        default:
                if (OPAQUE_TYPE_RANGE_UNASSIGNED(opaque_type))
                        name = "Unassigned";
@@ -589,30 +616,6 @@ static void free_opaque_info_per_type(void *val)
                ospf_opaque_lsa_flush_schedule(lsa);
        }
 
-       /* Remove "oipt" from its owner's self-originated LSA list. */
-       switch (oipt->lsa_type) {
-       case OSPF_OPAQUE_LINK_LSA: {
-               struct ospf_interface *oi =
-                       (struct ospf_interface *)(oipt->owner);
-               listnode_delete(oi->opaque_lsa_self, oipt);
-               break;
-       }
-       case OSPF_OPAQUE_AREA_LSA: {
-               struct ospf_area *area = (struct ospf_area *)(oipt->owner);
-               listnode_delete(area->opaque_lsa_self, oipt);
-               break;
-       }
-       case OSPF_OPAQUE_AS_LSA: {
-               struct ospf *top = (struct ospf *)(oipt->owner);
-               listnode_delete(top->opaque_lsa_self, oipt);
-               break;
-       }
-       default:
-               zlog_warn("free_opaque_info_per_type: Unexpected LSA-type(%u)",
-                         oipt->lsa_type);
-               break; /* This case may not exist. */
-       }
-
        OSPF_TIMER_OFF(oipt->t_opaque_lsa_self);
        list_delete_and_null(&oipt->id_list);
        XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
@@ -1786,7 +1789,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
                        lsa_type, delay,
                        GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)));
 
-       OSPF_OPAQUE_TIMER_ON(oipt->t_opaque_lsa_self, func, oipt, delay * 1000);
+       OSPF_OPAQUE_TIMER_ON(oipt->t_opaque_lsa_self, func, oipt, delay);
 
 out:
        return;
index 9dc1f92f4dc16ab0decc992dd94c3fdb1405342d..632b7b039e554285908f54ef2f8f21db69c587f7 100644 (file)
@@ -59,7 +59,9 @@
 #define OPAQUE_TYPE_L1VPN_LSA                          5
 #define OPAQUE_TYPE_ROUTER_INFORMATION_LSA             4
 #define OPAQUE_TYPE_INTER_AS_LSA                       6
-#define OPAQUE_TYPE_MAX                                6
+#define OPAQUE_TYPE_EXTENDED_PREFIX_LSA                7
+#define OPAQUE_TYPE_EXTENDED_LINK_LSA                  8
+#define OPAQUE_TYPE_MAX                                8
 
 /* Followings types are proposed in internet-draft documents. */
 #define OPAQUE_TYPE_8021_QOSPF                         129
@@ -120,6 +122,7 @@ enum lsa_opcode {
 
 extern void ospf_opaque_init(void);
 extern void ospf_opaque_term(void);
+extern void ospf_opaque_finish(void);
 extern int ospf_opaque_type9_lsa_init(struct ospf_interface *oi);
 extern void ospf_opaque_type9_lsa_term(struct ospf_interface *oi);
 extern int ospf_opaque_type10_lsa_init(struct ospf_area *area);
index ead6923435399ac3018a97a108df0a0bdb6049a1..7c7a6fd795cf79f555d2bf16c0d9af53672920b8 100644 (file)
@@ -3,9 +3,8 @@
  * with support of RFC5088 PCE Capabilites announcement
  *
  * Module name: Router Information
- * Version:     0.99.22
- * Created:     2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
  *
  * This file is part of GNU Quagga.
  *
@@ -39,6 +38,7 @@
 #include "thread.h"
 #include "hash.h"
 #include "sockunion.h" /* for inet_aton() */
+#include "mpls.h"
 
 #include "ospfd/ospfd.h"
 #include "ospfd/ospf_interface.h"
@@ -55,8 +55,8 @@
 #include "ospfd/ospf_route.h"
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
 #include "ospfd/ospf_ri.h"
-#include "ospfd/ospf_te.h"
 
 /* Store Router Information PCE TLV and SubTLV in network byte order. */
 struct ospf_pce_info {
@@ -69,6 +69,23 @@ struct ospf_pce_info {
        struct ri_pce_subtlv_cap_flag pce_cap_flag;
 };
 
+/*
+ * Store Router Information Segment Routing TLV and SubTLV
+ * in network byte order
+ */
+struct ospf_ri_sr_info {
+       bool enabled;
+       /* Algorithms supported by the node */
+       struct ri_sr_tlv_sr_algorithm algo;
+       /*
+        * Segment Routing Global Block i.e. label range
+        * Only one range supported in this code
+        */
+       struct ri_sr_tlv_sid_label_range range;
+       /* Maximum SID Depth supported by the node */
+       struct ri_sr_tlv_node_msd msd;
+};
+
 /* Following structure are internal use only. */
 struct ospf_router_info {
        bool enabled;
@@ -77,7 +94,7 @@ struct ospf_router_info {
        u_int8_t scope;
 
 /* Flags to manage this router information. */
-#define RIFLG_LSA_ENGAGED                      0x1
+#define RIFLG_LSA_ENGAGED              0x1
 #define RIFLG_LSA_FORCED_REFRESH       0x2
        u_int32_t flags;
 
@@ -90,6 +107,9 @@ struct ospf_router_info {
 
        /* Store PCE capability LSA */
        struct ospf_pce_info pce_info;
+
+       /* Store SR capability LSA */
+       struct ospf_ri_sr_info sr_info;
 };
 
 /*
@@ -113,15 +133,19 @@ static int ospf_router_info_lsa_originate(void *arg);
 static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa);
 static void ospf_router_info_lsa_schedule(enum lsa_opcode opcode);
 static void ospf_router_info_register_vty(void);
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa);
 static void del_pce_info(void *val);
 
 int ospf_router_info_init(void)
 {
 
+       zlog_info("RI -> Initialize Router Information");
+
        memset(&OspfRI, 0, sizeof(struct ospf_router_info));
        OspfRI.enabled = false;
        OspfRI.registered = 0;
        OspfRI.scope = OSPF_OPAQUE_AS_LSA;
+       OspfRI.area_id.s_addr = 0;
        OspfRI.flags = 0;
 
        /* Initialize pce domain and neighbor list */
@@ -131,6 +155,9 @@ int ospf_router_info_init(void)
        OspfRI.pce_info.pce_neighbor = list_new();
        OspfRI.pce_info.pce_neighbor->del = del_pce_info;
 
+       /* Initialize Segment Routing information structure */
+       OspfRI.sr_info.enabled = false;
+
        ospf_router_info_register_vty();
 
        return 0;
@@ -143,19 +170,22 @@ static int ospf_router_info_register(u_int8_t scope)
        if (OspfRI.registered)
                return rc;
 
-       zlog_info("Register Router Information with scope %s(%d)",
+       zlog_info("RI -> Register Router Information with scope %s(%d)",
                  scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope);
        rc = ospf_register_opaque_functab(
                scope, OPAQUE_TYPE_ROUTER_INFORMATION_LSA,
                NULL, /* new interface */
                NULL, /* del interface */
-               ospf_router_info_ism_change, ospf_router_info_nsm_change,
+               ospf_router_info_ism_change,
+               ospf_router_info_nsm_change,
                ospf_router_info_config_write_router,
                NULL, /* Config. write interface */
                NULL, /* Config. write debug */
-               ospf_router_info_show_info, ospf_router_info_lsa_originate,
-               ospf_router_info_lsa_refresh, NULL, /* new_lsa_hook */
-               NULL);                              /* del_lsa_hook */
+               ospf_router_info_show_info,
+               ospf_router_info_lsa_originate,
+               ospf_router_info_lsa_refresh,
+               ospf_router_info_lsa_update,
+               NULL); /* del_lsa_hook */
 
        if (rc != 0) {
                zlog_warn(
@@ -198,12 +228,35 @@ void ospf_router_info_term(void)
        return;
 }
 
+void ospf_router_info_finish(void)
+{
+       list_delete_all_node(OspfRI.pce_info.pce_domain);
+       list_delete_all_node(OspfRI.pce_info.pce_neighbor);
+
+       OspfRI.enabled = false;
+}
+
 static void del_pce_info(void *val)
 {
        XFREE(MTYPE_OSPF_PCE_PARAMS, val);
        return;
 }
 
+/* Catch RI LSA flooding Scope for ospf_ext.[h,c] code */
+struct scope_info ospf_router_info_get_flooding_scope(void)
+{
+       struct scope_info flooding_scope;
+
+       if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
+               flooding_scope.scope = OSPF_OPAQUE_AS_LSA;
+               flooding_scope.area_id.s_addr = 0;
+               return flooding_scope;
+       }
+       flooding_scope.scope = OSPF_OPAQUE_AREA_LSA;
+       flooding_scope.area_id.s_addr = OspfRI.area_id.s_addr;
+       return flooding_scope;
+}
+
 /*------------------------------------------------------------------------*
  * Followings are control functions for ROUTER INFORMATION parameters
  *management.
@@ -399,6 +452,78 @@ static void set_pce_cap_flag(u_int32_t cap, struct ospf_pce_info *pce)
        return;
 }
 
+/* Segment Routing TLV setter */
+
+/* Algorithm SubTLV - section 3.1 */
+static void set_sr_algorithm(uint8_t algo)
+{
+
+       OspfRI.sr_info.algo.value[0] = algo;
+       for (int i = 1; i < ALGORITHM_COUNT; i++)
+               OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+       /* Set TLV type and length == only 1 Algorithm */
+       TLV_TYPE(OspfRI.sr_info.algo) = htons(RI_SR_TLV_SR_ALGORITHM);
+       TLV_LEN(OspfRI.sr_info.algo) = htons(sizeof(uint8_t));
+
+}
+
+/* unset Aglogithm SubTLV */
+static void unset_sr_algorithm(uint8_t algo)
+{
+
+       for (int i = 0; i < ALGORITHM_COUNT; i++)
+               OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+       /* Unset TLV type and length */
+       TLV_TYPE(OspfRI.sr_info.algo) = htons(0);
+       TLV_LEN(OspfRI.sr_info.algo) = htons(0);
+
+}
+
+/* Segment Routing Global Block SubTLV - section 3.2 */
+static void set_sr_sid_label_range(struct sr_srgb srgb)
+{
+       /* Set Header */
+       TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE);
+       TLV_LEN(OspfRI.sr_info.range) =
+               htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t));
+       /* Set Range Size */
+       OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size));
+       /* Set Lower bound label SubTLV */
+       TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL);
+       TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH);
+       OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound));
+
+}
+
+/* Unset this SRGB SubTLV */
+static void unset_sr_sid_label_range(void)
+{
+
+       TLV_TYPE(OspfRI.sr_info.range) = htons(0);
+       TLV_LEN(OspfRI.sr_info.range) = htons(0);
+       TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0);
+       TLV_LEN(OspfRI.sr_info.range.lower) = htons(0);
+
+}
+
+/* Set Maximum Stack Depth for this router */
+static void set_sr_node_msd(uint8_t msd)
+{
+       TLV_TYPE(OspfRI.sr_info.msd) = htons(RI_SR_TLV_NODE_MSD);
+       TLV_LEN(OspfRI.sr_info.msd) = htons(sizeof(uint32_t));
+       OspfRI.sr_info.msd.value = msd;
+
+}
+
+/* Unset this router MSD */
+static void unset_sr_node_msd(void)
+{
+       TLV_TYPE(OspfRI.sr_info.msd) = htons(0);
+       TLV_LEN(OspfRI.sr_info.msd) = htons(0);
+
+}
 
 static void unset_param(struct tlv_header *tlv)
 {
@@ -466,11 +591,62 @@ static int is_mandated_params_set(struct ospf_router_info ori)
            && (ntohs(ori.pce_info.pce_cap_flag.header.type) == 0))
                return rc;
 
+       if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0)
+           && (ntohs(TLV_TYPE(ori.sr_info.range)) == 0))
+               return rc;
+
        rc = 1;
 
        return rc;
 }
 
+/*
+ * Used by Segment Routing to set new TLVs and Sub-TLVs values
+ *
+ * @param enable To activate or not Segment Routing router Information flooding
+ * @param size   Size of Label Range i.e. SRGB size
+ * @param lower  Lower bound of the Label Range i.e. SRGB first label
+ * @param msd    Maximum label Stack Depth suported by the router
+ *
+ * @return none
+ */
+void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd)
+{
+
+       /* First activate and initialize Router Information is necessary */
+       if (!OspfRI.enabled) {
+               OspfRI.enabled = true;
+               initialize_params(&OspfRI);
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("RI-> %s Routing Information for Segment Routing",
+                       enable ? "Enable" : "Disable");
+
+       /* Unset or Set SR parameters */
+       if (!enable) {
+               unset_sr_algorithm(SR_ALGORITHM_SPF);
+               unset_sr_sid_label_range();
+               unset_sr_node_msd();
+               OspfRI.sr_info.enabled = false;
+       } else {
+               // Only SR_ALGORITHM_SPF is supported
+               set_sr_algorithm(SR_ALGORITHM_SPF);
+               set_sr_sid_label_range(srgb);
+               if (msd != 0)
+                       set_sr_node_msd(msd);
+               else
+                       unset_sr_node_msd();
+               OspfRI.sr_info.enabled = true;
+       }
+
+       /* Refresh if already engaged or originate RI LSA */
+       if (CHECK_FLAG(OspfRI.flags, RIFLG_LSA_ENGAGED))
+               ospf_router_info_lsa_schedule(REFRESH_THIS_LSA);
+       else
+               ospf_router_info_lsa_schedule(REORIGINATE_THIS_LSA);
+}
+
 /*------------------------------------------------------------------------*
  * Followings are callback functions against generic Opaque-LSAs handling.
  *------------------------------------------------------------------------*/
@@ -519,12 +695,22 @@ static void ospf_router_info_lsa_body_set(struct stream *s)
        /* Build Router Information TLV */
        build_tlv(s, &OspfRI.router_cap.header);
 
-       /* Compute PCE Info header first */
-       set_pce_header (&OspfRI.pce_info);
+       /* Build Segment Routing TLVs if enabled */
+       if (OspfRI.sr_info.enabled) {
+               /* Build Algorithm TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo));
+               /* Build SRGB TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.range));
+               /* Build MSD TLV */
+               build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd));
+       }
 
        /* Add RI PCE TLV if it is set */
        if (OspfRI.pce_info.enabled) {
 
+               /* Compute PCE Info header first */
+               set_pce_header (&OspfRI.pce_info);
+
                /* Build PCE TLV */
                build_tlv_header(s, &OspfRI.pce_info.pce_header.header);
 
@@ -855,6 +1041,43 @@ static void ospf_router_info_lsa_schedule(enum lsa_opcode opcode)
        return;
 }
 
+/* Callback to handle Segment Routing information */
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa)
+{
+
+       /* Sanity Check */
+       if (lsa == NULL) {
+               zlog_warn("OSPF-RI (%s): Abort! LSA is NULL", __func__);
+               return -1;
+       }
+
+       /* Process only Opaque LSA */
+       if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+           && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+               return 0;
+
+       /* Process only Router Information LSA */
+       if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) !=
+                       OPAQUE_TYPE_ROUTER_INFORMATION_LSA)
+               return 0;
+
+       /* Check if it is not my LSA */
+       if (IS_LSA_SELF(lsa))
+               return 0;
+
+       /* Check if Router Info & Segment Routing are enable */
+       if (!OspfRI.enabled || !OspfRI.sr_info.enabled)
+               return 0;
+
+       /* Call Segment Routing LSA update or deletion */
+       if (!IS_LSA_MAXAGE(lsa))
+               ospf_sr_ri_lsa_update(lsa);
+       else
+               ospf_sr_ri_lsa_delete(lsa);
+
+       return 0;
+}
+
 /*------------------------------------------------------------------------*
  * Followings are vty session control functions.
  *------------------------------------------------------------------------*/
@@ -1021,6 +1244,99 @@ static u_int16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri,
        return sum;
 }
 
+/* Display Segment Routing Algorithm TLV information */
+static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_sr_algorithm *algo =
+               (struct ri_sr_tlv_sr_algorithm *)tlvh;
+       int i;
+
+       if (vty != NULL) {
+               vty_out(vty, "  Segment Routing Algorithm TLV:\n");
+               for (i = 0; i < ntohs(algo->header.length); i++) {
+                       switch (algo->value[i]) {
+                       case 0:
+                               vty_out(vty, "    Algorithm %d: SPF\n", i);
+                               break;
+                       case 1:
+                               vty_out(vty, "    Algorithm %d: Strict SPF\n",
+                                       i);
+                               break;
+                       default:
+                               vty_out(vty,
+                                       "  Algorithm %d: Unknown value %d\n", i,
+                                       algo->value[i]);
+                               break;
+                       }
+               }
+       }
+
+       else {
+               zlog_debug("  Segment Routing Algorithm TLV:\n");
+               for (i = 0; i < ntohs(algo->header.length); i++)
+                       switch (algo->value[i]) {
+                       case 0:
+                               zlog_debug("    Algorithm %d: SPF\n", i);
+                               break;
+                       case 1:
+                               zlog_debug("    Algorithm %d: Strict SPF\n", i);
+                               break;
+                       default:
+                               zlog_debug(
+                                       "    Algorithm %d: Unknown value %d\n",
+                                       i, algo->value[i]);
+                               break;
+                       }
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing SID/Label Range TLV information */
+static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_sid_label_range *range =
+               (struct ri_sr_tlv_sid_label_range *)tlvh;
+
+       if (vty != NULL) {
+               vty_out(vty,
+                       "  Segment Routing Range TLV:\n"
+                       "    Range Size = %d\n"
+                       "    SID Label = %d\n\n",
+                       GET_RANGE_SIZE(ntohl(range->size)),
+                       GET_LABEL(ntohl(range->lower.value)));
+       } else {
+               zlog_debug(
+                       "  Segment Routing Range TLV:\n"
+                       "    Range Size = %d\n"
+                       "    SID Label = %d\n\n",
+                       GET_RANGE_SIZE(ntohl(range->size)),
+                       GET_LABEL(ntohl(range->lower.value)));
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing Maximum Stack Depth TLV information */
+static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh)
+{
+       struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh;
+
+       if (vty != NULL) {
+               vty_out(vty,
+                       "  Segment Routing MSD TLV:\n"
+                       "    Node Maximum Stack Depth = %d\n",
+                       msd->value);
+       } else {
+               zlog_debug(
+                       "  Segment Routing MSD TLV:\n"
+                       "    Node Maximum Stack Depth = %d\n",
+                       msd->value);
+       }
+
+       return TLV_SIZE(tlvh);
+}
+
 static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
 {
        struct lsa_header *lsah = (struct lsa_header *)lsa->data;
@@ -1041,6 +1357,16 @@ static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
                        sum += TLV_HDR_SIZE;
                        sum += show_vty_pce_info(vty, tlvh, length - sum);
                        break;
+               case RI_SR_TLV_SR_ALGORITHM:
+                       sum += show_vty_sr_algorithm(vty, tlvh);
+                       break;
+               case RI_SR_TLV_SID_LABEL_RANGE:
+                       sum += show_vty_sr_range(vty, tlvh);
+                       break;
+               case RI_SR_TLV_NODE_MSD:
+                       sum += show_vty_sr_msd(vty, tlvh);
+                       break;
+
                default:
                        sum += show_vty_unknown_tlv(vty, tlvh);
                        break;
@@ -1058,53 +1384,54 @@ static void ospf_router_info_config_write_router(struct vty *vty)
        struct ri_pce_subtlv_neighbor *neighbor;
        struct in_addr tmp;
 
-       if (OspfRI.enabled) {
-               if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
-                       vty_out(vty, " router-info as\n");
-               else
-                       vty_out(vty, " router-info area %s\n",
-                               inet_ntoa(OspfRI.area_id));
-
-               if (OspfRI.pce_info.enabled) {
-
-                       if (pce->pce_address.header.type != 0)
-                               vty_out(vty, "  pce address %s\n",
-                                       inet_ntoa(pce->pce_address.address.value));
-
-                       if (pce->pce_cap_flag.header.type != 0)
-                               vty_out(vty, "  pce flag 0x%x\n",
-                                       ntohl(pce->pce_cap_flag.value));
-
-                       for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
-                               if (domain->header.type != 0) {
-                                       if (domain->type == PCE_DOMAIN_TYPE_AREA) {
-                                               tmp.s_addr = domain->value;
-                                               vty_out(vty, "  pce domain area %s\n",
-                                                       inet_ntoa(tmp));
-                                       } else {
-                                               vty_out(vty, "  pce domain as %d\n",
-                                                       ntohl(domain->value));
-                                       }
+       if (!OspfRI.enabled)
+               return;
+
+       if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+               vty_out(vty, " router-info as\n");
+       else
+               vty_out(vty, " router-info area %s\n",
+                       inet_ntoa(OspfRI.area_id));
+
+       if (OspfRI.pce_info.enabled) {
+
+               if (pce->pce_address.header.type != 0)
+                       vty_out(vty, "  pce address %s\n",
+                               inet_ntoa(pce->pce_address.address.value));
+
+               if (pce->pce_cap_flag.header.type != 0)
+                       vty_out(vty, "  pce flag 0x%x\n",
+                               ntohl(pce->pce_cap_flag.value));
+
+               for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
+                       if (domain->header.type != 0) {
+                               if (domain->type == PCE_DOMAIN_TYPE_AREA) {
+                                       tmp.s_addr = domain->value;
+                                       vty_out(vty, "  pce domain area %s\n",
+                                               inet_ntoa(tmp));
+                               } else {
+                                       vty_out(vty, "  pce domain as %d\n",
+                                               ntohl(domain->value));
                                }
                        }
+               }
 
-                       for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
-                               if (neighbor->header.type != 0) {
-                                       if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
-                                               tmp.s_addr = neighbor->value;
-                                               vty_out(vty, "  pce neighbor area %s\n",
-                                                       inet_ntoa(tmp));
-                                       } else {
-                                               vty_out(vty, "  pce neighbor as %d\n",
-                                                       ntohl(neighbor->value));
-                                       }
+               for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
+                       if (neighbor->header.type != 0) {
+                               if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
+                                       tmp.s_addr = neighbor->value;
+                                       vty_out(vty, "  pce neighbor area %s\n",
+                                               inet_ntoa(tmp));
+                               } else {
+                                       vty_out(vty, "  pce neighbor as %d\n",
+                                               ntohl(neighbor->value));
                                }
                        }
-
-                       if (pce->pce_scope.header.type != 0)
-                               vty_out(vty, "  pce scope 0x%x\n",
-                                       ntohl(OspfRI.pce_info.pce_scope.value));
                }
+
+               if (pce->pce_scope.header.type != 0)
+                       vty_out(vty, "  pce scope 0x%x\n",
+                               ntohl(OspfRI.pce_info.pce_scope.value));
        }
        return;
 }
@@ -1195,9 +1522,6 @@ DEFUN (no_router_info,
        if (CHECK_FLAG(OspfRI.flags, RIFLG_LSA_ENGAGED))
                ospf_router_info_lsa_schedule(FLUSH_THIS_LSA);
 
-       /* Unregister the callbacks */
-       ospf_router_info_unregister();
-
        OspfRI.enabled = false;
 
        return CMD_SUCCESS;
@@ -1539,7 +1863,7 @@ DEFUN (show_ip_opsf_router_info_pce,
        struct ri_pce_subtlv_domain *domain;
        struct ri_pce_subtlv_neighbor *neighbor;
 
-       if (OspfRI.enabled) {
+       if ((OspfRI.enabled) && (OspfRI.pce_info.enabled)) {
                vty_out(vty, "--- PCE parameters ---\n");
 
                if (pce->pce_address.header.type != 0)
@@ -1568,7 +1892,7 @@ DEFUN (show_ip_opsf_router_info_pce,
 
        } else {
                vty_out(vty,
-                       "  Router Information is disabled on this router\n");
+                       "  PCE info is disabled on this router\n");
        }
 
        return CMD_SUCCESS;
index 2d90730d93ecfde5411de5c5283ddca990932a95..39ebb720092a18663be3eb883def263ff034fad3 100644 (file)
@@ -1,11 +1,13 @@
 /*
  * This is an implementation of RFC4970 Router Information
  * with support of RFC5088 PCE Capabilites announcement
+ * and support of draft-ietf-ospf-segment-routing-extensions-18
+ * for Segment Routing Capabilities announcement
+ *
  *
  * Module name: Router Information
- * Version:     0.99.22
- * Created:     2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
  *
  * This file is part of GNU Zebra.
  *
@@ -33,7 +35,7 @@
  *
  *        24       16        8        0
  * +--------+--------+--------+--------+
- * |    1   |  MBZ   |........|........|
+ * |    4   |  MBZ   |........|........|
  * +--------+--------+--------+--------+
  * |<-Type->|<Resv'd>|<-- Instance --->|
  *
@@ -57,9 +59,8 @@
  * +--------+--------+--------+--------+  |
  * |   LS checksum   |     Length      |  V
  * +--------+--------+--------+--------+ ---
- * |      Type       |     Length      |  A
- * +--------+--------+--------+--------+  |  TLV part for Router Information;
- * Values might be
+ * |      Type       |     Length      |  A  TLV part for Router Information;
+ * +--------+--------+--------+--------+  |  Values might be
  * |              Values ...           |  V  structured as a set of sub-TLVs.
  * +--------+--------+--------+--------+ ---
  */
@@ -68,9 +69,9 @@
  * Following section defines TLV body parts.
  */
 
-/* Up to now, 8 code point have been assigned to Router Information */
+/* Up to now, 11 code points have been assigned to Router Information */
 /* Only type 1 Router Capabilities and 6 PCE are supported with this code */
-#define RI_IANA_MAX_TYPE               8
+#define RI_IANA_MAX_TYPE               11
 
 /* RFC4970: Router Information Capabilities TLV */ /* Mandatory */
 #define RI_TLV_CAPABILITIES            1
@@ -80,12 +81,13 @@ struct ri_tlv_router_cap {
        u_int32_t value;
 };
 
-#define RI_GRACE_RESTART       0x01
-#define RI_GRACE_HELPER                0x02
-#define RI_STUB_SUPPORT                0x04
-#define RI_TE_SUPPORT          0x08
-#define RI_P2P_OVER_LAN                0x10
-#define RI_TE_EXPERIMENTAL     0x20
+/* Capabilities bits are left align */
+#define RI_GRACE_RESTART       0x80000000
+#define RI_GRACE_HELPER                0x40000000
+#define RI_STUB_SUPPORT                0x20000000
+#define RI_TE_SUPPORT          0x10000000
+#define RI_P2P_OVER_LAN                0x08000000
+#define RI_TE_EXPERIMENTAL     0x04000000
 
 #define RI_TLV_LENGTH          4
 
@@ -151,22 +153,32 @@ struct ri_pce_subtlv_neighbor {
 #define RI_PCE_SUBTLV_CAP_FLAG         5
 
 #define PCE_CAP_GMPLS_LINK             0x0001
-#define PCE_CAP_BIDIRECTIONAL  0x0002
-#define PCE_CAP_DIVERSE_PATH   0x0004
-#define PCE_CAP_LOAD_BALANCE   0x0008
-#define PCE_CAP_SYNCHRONIZED   0x0010
+#define PCE_CAP_BIDIRECTIONAL          0x0002
+#define PCE_CAP_DIVERSE_PATH           0x0004
+#define PCE_CAP_LOAD_BALANCE           0x0008
+#define PCE_CAP_SYNCHRONIZED           0x0010
 #define PCE_CAP_OBJECTIVES             0x0020
 #define PCE_CAP_ADDITIVE               0x0040
-#define PCE_CAP_PRIORIZATION   0x0080
-#define PCE_CAP_MULTIPLE_REQ   0x0100
+#define PCE_CAP_PRIORIZATION           0x0080
+#define PCE_CAP_MULTIPLE_REQ           0x0100
 
 struct ri_pce_subtlv_cap_flag {
        struct tlv_header header; /* Type = 5; Length = n x 4 bytes. */
        u_int32_t value;
 };
 
+/* Structure to share flooding scope info for Segment Routing */
+struct scope_info {
+       uint8_t scope;
+       struct in_addr area_id;
+};
+
 /* Prototypes. */
 extern int ospf_router_info_init(void);
 extern void ospf_router_info_term(void);
-
+extern void ospf_router_info_finish(void);
+extern int ospf_router_info_enable(void);
+extern void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb,
+                                      uint8_t msd);
+extern struct scope_info ospf_router_info_get_flooding_scope(void);
 #endif /* _ZEBRA_OSPF_ROUTER_INFO_H */
index 22fff1b53d50b0d603986de89a1ccf51c0e78081..9c747cd56562fad1e226da6758c12d5137e59299 100644 (file)
@@ -46,6 +46,7 @@
 #include "ospfd/ospf_ase.h"
 #include "ospfd/ospf_abr.h"
 #include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_sr.h"
 
 /* Variables to ensure a SPF scheduled log message is printed only once */
 
@@ -1339,7 +1340,6 @@ static int ospf_spf_calculate_timer(struct thread *thread)
 
        ospf_ase_calculate_timer_add(ospf);
 
-
        if (IS_DEBUG_OSPF_EVENT)
                zlog_debug("%s: ospf install new route, vrf %s id %u new_table count %lu",
                           __PRETTY_FUNCTION__,
@@ -1366,6 +1366,9 @@ static int ospf_spf_calculate_timer(struct thread *thread)
                ospf_abr_task(ospf);
        abr_time = monotime_since(&start_time, NULL);
 
+       /* Schedule Segment Routing update */
+       ospf_sr_update_timer_add(ospf);
+
        total_spf_time =
                monotime_since(&spf_start_time, &ospf->ts_spf_duration);
 
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
new file mode 100644 (file)
index 0000000..725bf95
--- /dev/null
@@ -0,0 +1,2281 @@
+/*
+ * This is an implementation of Segment Routing
+ * as per draft draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "if.h"
+#include "jhash.h"
+#include "libospf.h" /* for ospf interface types */
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "monotime.h"
+#include "network.h"
+#include "prefix.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "stream.h"
+#include "table.h"
+#include "thread.h"
+#include "vty.h"
+#include "zclient.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
+#include "ospfd/ospf_zebra.h"
+
+/*
+ * Global variable to manage Segment Routing on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_sr_db OspfSR;
+static void ospf_sr_register_vty(void);
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe);
+
+/*
+ * Segment Routing Data Base functions
+ */
+
+/* Hash function for Segment Routing entry */
+static unsigned int sr_hash(void *p)
+{
+       const struct in_addr *rid = p;
+
+       return jhash_1word(rid->s_addr, 0);
+}
+
+/* Compare 2 Router ID hash entries based on SR Node */
+static int sr_cmp(const void *p1, const void *p2)
+{
+       const struct sr_node *srn = p1;
+       const struct in_addr *rid = p2;
+
+       return IPV4_ADDR_SAME(&srn->adv_router, rid);
+}
+
+/* Functions to remove an SR Link */
+static void del_sr_link(void *val)
+{
+       struct sr_link *srl = (struct sr_link *)val;
+
+       del_sid_nhlfe(srl->nhlfe[0]);
+       del_sid_nhlfe(srl->nhlfe[1]);
+       XFREE(MTYPE_OSPF_SR_PARAMS, val);
+
+}
+
+/* Functions to remove an SR Prefix */
+static void del_sr_pref(void *val)
+{
+       struct sr_prefix *srp = (struct sr_prefix *)val;
+
+       del_sid_nhlfe(srp->nhlfe);
+       XFREE(MTYPE_OSPF_SR_PARAMS, val);
+
+}
+
+/* Allocate new Segment Routine node */
+static struct sr_node *sr_node_new(struct in_addr *rid)
+{
+
+       if (rid == NULL)
+               return NULL;
+
+       struct sr_node *new;
+
+       /* Allocate Segment Routing node memory */
+       new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_node));
+
+       /* Sanity Check */
+       if (new == NULL) {
+               zlog_err("SR (%s): Abort! can't create new SR node", __func__);
+               return NULL;
+       }
+
+       /* Default Algorithm, SRGB and MSD */
+       for (int i = 0; i < ALGORITHM_COUNT; i++)
+               new->algo[i] = SR_ALGORITHM_UNSET;
+
+       new->srgb.range_size = 0;
+       new->srgb.lower_bound = 0;
+       new->msd = 0;
+
+       /* Create Link, Prefix and Range TLVs list */
+       new->ext_link = list_new();
+       new->ext_prefix = list_new();
+       new->ext_link->del = del_sr_link;
+       new->ext_prefix->del = del_sr_pref;
+
+       /* Check if list are correctly created */
+       if (new->ext_link == NULL || new->ext_prefix == NULL) {
+               list_delete_original(new->ext_link);
+               list_delete_original(new->ext_prefix);
+               XFREE(MTYPE_OSPF_SR_PARAMS, new);
+               return NULL;
+       }
+
+       IPV4_ADDR_COPY(&new->adv_router, rid);
+       new->neighbor = NULL;
+       new->instance = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Created new SR node for %s",
+                          inet_ntoa(new->adv_router));
+       return new;
+}
+
+/* Delete Segment Routing node */
+static void sr_node_del(struct sr_node *srn)
+{
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       /* Clean Extended Link */
+       list_delete_and_null(&srn->ext_link);
+
+       /* Clean Prefix List */
+       list_delete_and_null(&srn->ext_prefix);
+
+       XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+}
+
+/* Get SR Node for a given nexthop */
+static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf,
+                                             struct in_addr nexthop)
+{
+       struct ospf_interface *oi = NULL;
+       struct ospf_neighbor *nbr = NULL;
+       struct listnode *node;
+       struct route_node *rn;
+       struct sr_node *srn;
+       bool found;
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL)
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Search SR-Node for nexthop %s",
+                          inet_ntoa(nexthop));
+
+       /* First, search neighbor Router ID for this nexthop */
+       found = false;
+       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
+               for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+                       nbr = rn->info;
+                       if ((nbr) && (IPV4_ADDR_SAME(&nexthop, &nbr->src))) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (found)
+                       break;
+       }
+
+       if (!found)
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Found nexthop Router ID %s",
+                          inet_ntoa(nbr->router_id));
+       /* Then, search SR Node */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id);
+
+       return srn;
+}
+
+/*
+ * Segment Routing Initialization functions
+ */
+
+/* Segment Routing starter function */
+static int ospf_sr_start(struct ospf *ospf)
+{
+       struct route_node *rn;
+       struct ospf_lsa *lsa;
+       struct sr_node *srn;
+       int rc = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (%s): Start Segment Routing", __func__);
+
+       /* Initialize self SR Node */
+       srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id),
+                      (void *)sr_node_new);
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return rc;
+
+       /* Complete & Store self SR Node */
+       srn->srgb.range_size = OspfSR.srgb.range_size;
+       srn->srgb.lower_bound = OspfSR.srgb.lower_bound;
+       srn->algo[0] = OspfSR.algo[0];
+       srn->msd = OspfSR.msd;
+       OspfSR.self = srn;
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR (%s): Update SR-DB from LSDB", __func__);
+
+       /* Start by looking to Router Info & Extended LSA in lsdb */
+       if ((ospf != NULL) && (ospf->backbone != NULL)) {
+               LSDB_LOOP(OPAQUE_AREA_LSDB(ospf->backbone), rn, lsa)
+               {
+                       if (IS_LSA_MAXAGE(lsa) || IS_LSA_SELF(lsa))
+                               continue;
+                       int lsa_id =
+                               GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+                       switch (lsa_id) {
+                       case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+                               ospf_sr_ri_lsa_update(lsa);
+                               break;
+                       case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+                               ospf_sr_ext_prefix_lsa_update(lsa);
+                               break;
+                       case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+                               ospf_sr_ext_link_lsa_update(lsa);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       rc = 1;
+       return rc;
+}
+
+/* Stop Segment Routing */
+static void ospf_sr_stop(void)
+{
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (%s): Stop Segment Routing", __func__);
+
+       /*
+        * Remove all SR Nodes from the Hash table. Prefix and Link SID will
+        * be remove though list_delete_and_null() call. See sr_node_del()
+        */
+       hash_clean(OspfSR.neighbors, (void *)sr_node_del);
+}
+
+/*
+ * Segment Routing initialize function
+ *
+ * @param - nothing
+ *
+ * @return 0 if OK, -1 otherwise
+ */
+int ospf_sr_init(void)
+{
+       int rc = -1;
+
+       zlog_info("SR (%s): Initialize SR Data Base", __func__);
+
+       memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
+       OspfSR.enabled = false;
+       /* Only AREA flooding is supported in this release */
+       OspfSR.scope = OSPF_OPAQUE_AREA_LSA;
+
+       /* Initialize SRGB, Algorithms and MSD TLVs */
+       /* Only Algorithm SPF is supported */
+       OspfSR.algo[0] = SR_ALGORITHM_SPF;
+       for (int i = 1; i < ALGORITHM_COUNT; i++)
+               OspfSR.algo[i] = SR_ALGORITHM_UNSET;
+
+       OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE;
+       OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+       OspfSR.msd = 0;
+
+       /* Initialize Hash table for neighbor SR nodes */
+       OspfSR.neighbors = hash_create(sr_hash, sr_cmp, "OSPF_SR");
+       if (OspfSR.neighbors == NULL)
+               return rc;
+
+       /* Initialize Route Table for prefix */
+       OspfSR.prefix = route_table_init();
+       if (OspfSR.prefix == NULL)
+               return rc;
+
+       /* Register Segment Routing VTY command */
+       ospf_sr_register_vty();
+
+       rc = 0;
+       return rc;
+}
+
+/*
+ * Segment Routing termination function
+ *
+ * @param - nothing
+ * @return - nothing
+ */
+void ospf_sr_term(void)
+{
+
+       /* Stop Segment Routing */
+       ospf_sr_stop();
+
+       /* Clear SR Node Table */
+       if (OspfSR.neighbors)
+               hash_free(OspfSR.neighbors);
+
+       /* Clear Prefix Table */
+       if (OspfSR.prefix)
+               route_table_finish(OspfSR.prefix);
+
+       OspfSR.enabled = false;
+       OspfSR.self = NULL;
+}
+
+/*
+ * Segment Routing finish function
+ *
+ * @param - nothing
+ * @return - nothing
+ */
+void ospf_sr_finish(void)
+{
+       /* Stop Segment Routing */
+       ospf_sr_stop();
+
+       OspfSR.enabled = false;
+}
+
+/*
+ * Following functions are used to manipulate the
+ * Next Hop Label Forwarding entry (NHLFE)
+ */
+
+/* Compute label from index */
+static mpls_label_t index2label(uint32_t index, struct sr_srgb srgb)
+{
+       mpls_label_t label;
+
+       label = srgb.lower_bound + index;
+       if (label > (srgb.lower_bound + srgb.range_size))
+               return MPLS_INVALID_LABEL;
+       else
+               return label;
+}
+
+/* Get neighbor full structure from address */
+static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
+                                                 struct in_addr addr)
+{
+       struct ospf_neighbor *nbr;
+       struct ospf_interface *oi;
+       struct listnode *node;
+       struct route_node *rn;
+
+       /* Sanity Check */
+       if (top == NULL)
+               return NULL;
+
+       for (ALL_LIST_ELEMENTS_RO(top->oiflist, node, oi))
+               for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+                       nbr = rn->info;
+                       if (nbr)
+                               if (IPV4_ADDR_SAME(&nbr->address.u.prefix4,
+                                                  &addr)
+                                   || IPV4_ADDR_SAME(&nbr->router_id, &addr)) {
+                                       route_unlock_node(rn);
+                                       return nbr;
+                               }
+               }
+       return NULL;
+}
+
+/* Get OSPF Path from address */
+static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
+                                            struct prefix_ipv4 p)
+{
+       struct ospf_route *or;
+       struct ospf_path *path;
+       struct listnode *node;
+       struct route_node *rn;
+
+       /* Sanity Check */
+       if ((top == NULL) && (top->new_table))
+               return NULL;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("      |-  Search Nexthop for prefix %s/%u",
+                          inet_ntoa(p.prefix), p.prefixlen);
+
+       rn = route_node_lookup(top->new_table, (struct prefix *)&p);
+
+       /*
+        * Check if we found an OSPF route. May be NULL if SPF has not
+        * yet populate routing table for this prefix.
+        */
+       if (rn == NULL)
+               return NULL;
+
+       route_unlock_node(rn);
+       or = rn->info;
+       if (or == NULL)
+               return NULL;
+
+       /* Then search path from this route */
+       for (ALL_LIST_ELEMENTS_RO(or->paths, node, path))
+               if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0)
+                       return path;
+
+       return NULL;
+}
+
+/* Compute NHLFE entry for Extended Link */
+static int compute_link_nhlfe(struct sr_link *srl)
+{
+       struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       struct ospf_neighbor *nh;
+       int rc = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Compute NHLFE for link %s/%u",
+                          inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+                          srl->nhlfe[0].prefv4.prefixlen);
+
+       /* First determine the OSPF Neighbor */
+       nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop);
+
+       /* Neighbor could be not found when OSPF Adjacency just fire up
+        * because SPF don't yet populate routing table. This NHLFE will
+        * be fixed later when SR SPF schedule will be called.
+        */
+       if (nh == NULL)
+               return rc;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found nexthop NHLFE %s",
+                          inet_ntoa(nh->router_id));
+
+       /* Set ifindex for this neighbor */
+       srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex;
+       srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex;
+
+       /* Set Input & Output Label */
+       if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+               srl->nhlfe[0].label_in = srl->sid[0];
+       else
+               srl->nhlfe[0].label_in =
+                       index2label(srl->sid[0], srl->srn->srgb);
+       if (CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+               srl->nhlfe[1].label_in = srl->sid[1];
+       else
+               srl->nhlfe[1].label_in =
+                       index2label(srl->sid[1], srl->srn->srgb);
+
+       srl->nhlfe[0].label_out = MPLS_IMP_NULL_LABEL;
+       srl->nhlfe[1].label_out = MPLS_IMP_NULL_LABEL;
+
+       rc = 1;
+       return rc;
+}
+
+/*
+ * Compute NHLFE entry for Extended Prefix
+ *
+ * @param srp - Segment Routing Prefix
+ *
+ * @return -1 if next hop is not found, 0 if nexthop has not changed
+ *         and 1 if success
+ */
+static int compute_prefix_nhlfe(struct sr_prefix *srp)
+{
+       struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+       struct ospf_path *nh = NULL;
+       struct sr_node *srnext;
+       int rc = -1;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Compute NHLFE for prefix %s/%u",
+                          inet_ntoa(srp->nhlfe.prefv4.prefix),
+                          srp->nhlfe.prefv4.prefixlen);
+
+       /* First determine the nexthop */
+       nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4);
+
+       /* Nexthop could be not found when OSPF Adjacency just fire up
+        * because SPF don't yet populate routing table. This NHLFE will
+        * be fixed later when SR SPF schedule will be called.
+        */
+       if (nh == NULL)
+               return rc;
+
+       /* Check if NextHop has changed when call after running a new SPF */
+       if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop)
+           && (nh->ifindex == srp->nhlfe.ifindex))
+               return 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found new next hop for this NHLFE: %s",
+                          inet_ntoa(nh->nexthop));
+
+       /*
+        * Get SR-Node for this nexthop. Could be not yet available
+        * as Extende Link / Prefix and Router Information are flooded
+        * after LSA Type 1 & 2 which populate the OSPF Route Table
+        */
+       srnext = get_sr_node_by_nexthop(top, nh->nexthop);
+       if (srnext == NULL)
+               return rc;
+
+       /* And store this information for later update if SR Node is found */
+       srnext->neighbor = OspfSR.self;
+       if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router))
+               srp->nexthop = NULL;
+       else
+               srp->nexthop = srnext;
+
+       /*
+        * SR Node could be known, but SRGB could be not initialize
+        * This is due to the fact that Extended Link / Prefix could
+        * be received before corresponding Router Information LSA
+        */
+       if ((srnext == NULL) || (srnext->srgb.lower_bound == 0)
+           || (srnext->srgb.range_size == 0))
+               return rc;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Found SRGB %u/%u for next hop SR-Node %s",
+                          srnext->srgb.range_size, srnext->srgb.lower_bound,
+                          inet_ntoa(srnext->adv_router));
+
+       /* Set ip addr & ifindex for this neighbor */
+       IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop);
+       srp->nhlfe.ifindex = nh->ifindex;
+
+       /* Compute Input Label with self SRGB */
+       srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb);
+       /*
+        * and Output Label with Next hop SR Node SRGB or Implicit Null label
+        * if next hop is the destination and request PHP
+        */
+       if ((srp->nexthop == NULL)
+           && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+               srp->nhlfe.label_out = MPLS_IMP_NULL_LABEL;
+       else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+               srp->nhlfe.label_out = srp->sid;
+       else
+               srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  Computed new labels in: %u out: %u",
+                          srp->nhlfe.label_in, srp->nhlfe.label_out);
+
+       rc = 1;
+       return rc;
+}
+
+/* Send MPLS Label entry to Zebra for installation or deletion */
+static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe)
+{
+       struct stream *s;
+
+       /* Reset stream. */
+       s = zclient->obuf;
+       stream_reset(s);
+
+       zclient_create_header(s, cmd, VRF_DEFAULT);
+       stream_putc(s, ZEBRA_LSP_SR);
+       /* OSPF Segment Routing currently support only IPv4 */
+       stream_putl(s, nhlfe.prefv4.family);
+       stream_put_in_addr(s, &nhlfe.prefv4.prefix);
+       stream_putc(s, nhlfe.prefv4.prefixlen);
+       stream_put_in_addr(s, &nhlfe.nexthop);
+       stream_putl(s, nhlfe.ifindex);
+       stream_putc(s, OSPF_SR_PRIORITY_DEFAULT);
+       stream_putl(s, nhlfe.label_in);
+       stream_putl(s, nhlfe.label_out);
+
+       /* Put length at the first point of the stream. */
+       stream_putw_at(s, 0, stream_get_endp(s));
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  %s LSP %u/%u for %s/%u via %u",
+                          cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
+                          nhlfe.label_in, nhlfe.label_out,
+                          inet_ntoa(nhlfe.prefv4.prefix),
+                          nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+       return zclient_send_message(zclient);
+}
+
+/* Request zebra to install/remove FEC in FIB */
+static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe)
+{
+       struct zapi_route api;
+       struct zapi_nexthop *api_nh;
+
+       /* Support only IPv4 */
+       if (nhlfe.prefv4.family != AF_INET)
+               return -1;
+
+       memset(&api, 0, sizeof(api));
+       api.vrf_id = VRF_DEFAULT;
+       api.type = ZEBRA_ROUTE_OSPF;
+       api.safi = SAFI_UNICAST;
+       memcpy(&api.prefix, &nhlfe.prefv4, sizeof(struct prefix_ipv4));
+
+       if (cmd == ZEBRA_ROUTE_ADD) {
+               /* Metric value. */
+               SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+               api.metric = OSPF_SR_DEFAULT_METRIC;
+               /* Nexthop */
+               SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+               api_nh = &api.nexthops[0];
+               IPV4_ADDR_COPY(&api_nh->gate.ipv4, &nhlfe.nexthop);
+               api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+               api_nh->ifindex = nhlfe.ifindex;
+               /* MPLS labels */
+               SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
+               api_nh->labels[0] = nhlfe.label_out;
+               api_nh->label_num = 1;
+               api.nexthop_num = 1;
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("    |-  %s FEC %u for %s/%u via %u",
+                          cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete",
+                          nhlfe.label_out, inet_ntoa(nhlfe.prefv4.prefix),
+                          nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+       return zclient_route_send(cmd, zclient, &api);
+}
+
+/* Add new NHLFE entry for SID */
+static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+       if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+               ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe);
+               if (nhlfe.label_out != MPLS_IMP_NULL_LABEL)
+                       ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_ADD, nhlfe);
+       }
+}
+
+/* Remove NHLFE entry for SID */
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+       if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+               ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe);
+               if (nhlfe.label_out != MPLS_IMP_NULL_LABEL)
+                       ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_DELETE, nhlfe);
+       }
+}
+
+/* Update NHLFE entry for SID */
+static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2)
+{
+
+       del_sid_nhlfe(n1);
+       add_sid_nhlfe(n2);
+}
+
+/*
+ * Functions to parse and get Extended Link / Prefix
+ * TLVs and SubTLVs
+ */
+
+/* Extended Link SubTLVs Getter */
+static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh)
+{
+
+       struct sr_link *srl;
+       struct ext_tlv_link *link = (struct ext_tlv_link *)tlvh;
+       struct ext_subtlv_adj_sid *adj_sid;
+       struct ext_subtlv_lan_adj_sid *lan_sid;
+       struct ext_subtlv_rmt_itf_addr *rmt_itf;
+
+       struct tlv_header *sub_tlvh;
+       uint16_t length = 0, sum = 0, i = 0;
+
+       srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link));
+
+       if (srl == NULL)
+               return NULL;
+
+       /* Initialize TLV browsing */
+       length = ntohs(tlvh->length) - EXT_TLV_LINK_SIZE;
+       sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+                                        + EXT_TLV_LINK_SIZE);
+       for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+               switch (ntohs(sub_tlvh->type)) {
+               case EXT_SUBTLV_ADJ_SID:
+                       adj_sid = (struct ext_subtlv_adj_sid *)sub_tlvh;
+                       srl->type = ADJ_SID;
+                       i = CHECK_FLAG(adj_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+                                   ? 1
+                                   : 0;
+                       srl->flags[i] = adj_sid->flags;
+                       if (CHECK_FLAG(adj_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+                               srl->sid[i] = GET_LABEL(ntohl(adj_sid->value));
+                       else
+                               srl->sid[i] = ntohl(adj_sid->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop, &link->link_id);
+                       break;
+               case EXT_SUBTLV_LAN_ADJ_SID:
+                       lan_sid = (struct ext_subtlv_lan_adj_sid *)sub_tlvh;
+                       srl->type = LAN_ADJ_SID;
+                       i = CHECK_FLAG(lan_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+                                   ? 1
+                                   : 0;
+                       srl->flags[i] = lan_sid->flags;
+                       if (CHECK_FLAG(lan_sid->flags,
+                                      EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+                               srl->sid[i] = GET_LABEL(ntohl(lan_sid->value));
+                       else
+                               srl->sid[i] = ntohl(lan_sid->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop,
+                                      &lan_sid->neighbor_id);
+                       break;
+               case EXT_SUBTLV_RMT_ITF_ADDR:
+                       rmt_itf = (struct ext_subtlv_rmt_itf_addr *)sub_tlvh;
+                       IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &rmt_itf->value);
+                       IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &rmt_itf->value);
+                       break;
+               default:
+                       break;
+               }
+               sum += TLV_SIZE(sub_tlvh);
+       }
+
+       IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data);
+       srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+       srl->nhlfe[0].prefv4.family = AF_INET;
+       apply_mask_ipv4(&srl->nhlfe[0].prefv4);
+       IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data);
+       srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+       srl->nhlfe[1].prefv4.family = AF_INET;
+       apply_mask_ipv4(&srl->nhlfe[1].prefv4);
+
+       if (IS_DEBUG_OSPF_SR) {
+               zlog_debug("  |-  Found primary Adj/Lan Sid %u for %s/%u",
+                          srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+                          srl->nhlfe[0].prefv4.prefixlen);
+               zlog_debug("  |-  Found backup Adj/Lan Sid %u for %s/%u",
+                          srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix),
+                          srl->nhlfe[1].prefv4.prefixlen);
+       }
+
+       return srl;
+}
+
+/* Extended Prefix SubTLVs Getter */
+static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh)
+{
+
+       struct sr_prefix *srp;
+       struct ext_tlv_prefix *pref = (struct ext_tlv_prefix *)tlvh;
+       struct ext_subtlv_prefix_sid *psid;
+
+       struct tlv_header *sub_tlvh;
+       uint16_t length = 0, sum = 0;
+
+       srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+
+       if (srp == NULL)
+               return NULL;
+
+       /* Initialize TLV browsing */
+       length = ntohs(tlvh->length) - EXT_TLV_PREFIX_SIZE;
+       sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+                                        + EXT_TLV_PREFIX_SIZE);
+       for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+               switch (ntohs(sub_tlvh->type)) {
+               case EXT_SUBTLV_PREFIX_SID:
+                       psid = (struct ext_subtlv_prefix_sid *)sub_tlvh;
+                       if (psid->algorithm != SR_ALGORITHM_SPF) {
+                               zlog_err(
+                                       "SR (%s): Unsupported Algorithm",
+                                       __func__);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+                               return NULL;
+                       }
+                       srp->type = PREF_SID;
+                       srp->flags = psid->flags;
+                       if (CHECK_FLAG(psid->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+                               srp->sid = GET_LABEL(ntohl(psid->value));
+                       else
+                               srp->sid = ntohl(psid->value);
+                       IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
+                                      &pref->address);
+                       srp->nhlfe.prefv4.prefixlen = pref->pref_length;
+                       srp->nhlfe.prefv4.family = AF_INET;
+                       apply_mask_ipv4(&srp->nhlfe.prefv4);
+                       break;
+               default:
+                       break;
+               }
+               sum += TLV_SIZE(sub_tlvh);
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Found SID %u for prefix %s/%u", srp->sid,
+                          inet_ntoa(srp->nhlfe.prefv4.prefix),
+                          srp->nhlfe.prefv4.prefixlen);
+       return srp;
+}
+
+/*
+ * Functions to manipulate Segment Routing Link & Prefix structures
+ */
+
+/* Compare two Segment Link: return 0 if equal, 1 otherwise */
+static inline int sr_link_cmp(struct sr_link *srl1, struct sr_link *srl2)
+{
+       if ((srl1->sid[0] == srl2->sid[0]) && (srl1->sid[1] == srl2->sid[1])
+           && (srl1->type == srl2->type) && (srl1->flags[0] == srl2->flags[0])
+           && (srl1->flags[1] == srl2->flags[1]))
+               return 0;
+       else
+               return 1;
+}
+
+/* Compare two Segment Prefix: return 0 if equal, 1 otherwise */
+static inline int sr_prefix_cmp(struct sr_prefix *srp1, struct sr_prefix *srp2)
+{
+       if ((srp1->sid == srp2->sid) && (srp1->flags == srp2->flags))
+               return 0;
+       else
+               return 1;
+}
+
+/* Update Segment Link of given Segment Routing Node */
+static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
+                               u_char lsa_flags)
+{
+       struct listnode *node;
+       struct sr_link *lk;
+       bool found = false;
+
+       /* Sanity check */
+       if ((srn == NULL) || (srl == NULL))
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Process Extended Link Adj/Lan-SID");
+
+       /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */
+       if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+           || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+           || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF))
+               return;
+
+       /* Search for existing Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, lk))
+               if (lk->instance == srl->instance) {
+                       found = true;
+                       break;
+               }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  %s SR Link 8.0.0.%u for SR node %s",
+                          found ? "Update" : "Add",
+                          GET_OPAQUE_ID(srl->instance),
+                          inet_ntoa(srn->adv_router));
+
+       /* if not found, add new Segment Link and install NHLFE */
+       if (!found) {
+               /* Complete SR-Link and add it to SR-Node list */
+               srl->srn = srn;
+               IPV4_ADDR_COPY(&srl->adv_router, &srn->adv_router);
+               listnode_add(srn->ext_link, srl);
+               /* Try to set MPLS table */
+               if (compute_link_nhlfe(srl)) {
+                       add_sid_nhlfe(srl->nhlfe[0]);
+                       add_sid_nhlfe(srl->nhlfe[1]);
+               }
+       } else {
+               if (sr_link_cmp(lk, srl)) {
+                       if (compute_link_nhlfe(srl)) {
+                               update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]);
+                               update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]);
+                               /* Replace Segment List */
+                               listnode_delete(srn->ext_link, lk);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, lk);
+                               srl->srn = srn;
+                               IPV4_ADDR_COPY(&srl->adv_router,
+                                              &srn->adv_router);
+                               listnode_add(srn->ext_link, srl);
+                       } else {
+                               /* New NHLFE was not found.
+                                * Just free the SR Link
+                                */
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+                       }
+               } else {
+                       /*
+                        * This is just an LSA refresh.
+                        * Stop processing and free SR Link
+                        */
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+               }
+       }
+}
+
+/* Update Segment Prefix of given Segment Routing Node */
+static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
+{
+
+       struct listnode *node;
+       struct sr_prefix *pref;
+       bool found = false;
+
+       /* Sanity check */
+       if (srn == NULL || srp == NULL)
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Process Extended Prefix SID %u", srp->sid);
+
+       /* Process only Global Prefix SID */
+       if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG))
+               return;
+
+       /* Search for existing Segment Prefix */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, pref))
+               if (pref->instance == srp->instance) {
+                       found = true;
+                       break;
+               }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  %s SR LSA ID 7.0.0.%u for SR node %s",
+                          found ? "Update" : "Add",
+                          GET_OPAQUE_ID(srp->instance),
+                          inet_ntoa(srn->adv_router));
+
+       /* if not found, add new Segment Prefix and install NHLFE */
+       if (!found) {
+               /* Complete SR-Prefix and add it to SR-Node list */
+               srp->srn = srn;
+               IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router);
+               listnode_add(srn->ext_prefix, srp);
+               /* Try to set MPLS table */
+               if (compute_prefix_nhlfe(srp) == 1)
+                       add_sid_nhlfe(srp->nhlfe);
+       } else {
+               if (sr_prefix_cmp(pref, srp)) {
+                       if (compute_prefix_nhlfe(srp) == 1) {
+                               update_sid_nhlfe(pref->nhlfe, srp->nhlfe);
+                               /* Replace Segment Prefix */
+                               listnode_delete(srn->ext_prefix, pref);
+                               XFREE(MTYPE_OSPF_SR_PARAMS, pref);
+                               srp->srn = srn;
+                               IPV4_ADDR_COPY(&srp->adv_router,
+                                              &srn->adv_router);
+                               listnode_add(srn->ext_prefix, srp);
+                       } else {
+                               /* New NHLFE was not found.
+                                * Just free the SR Prefix
+                                */
+                               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+                       }
+               } else {
+                       /* This is just an LSA refresh.
+                        * Stop processing and free SR Prefix
+                        */
+                       XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+               }
+       }
+}
+
+/*
+ * When change the FRR Self SRGB, update the NHLFE Input Label
+ * for all Extended Prefix with SID index through hash_iterate()
+ */
+static void update_in_nhlfe(struct hash_backet *backet, void *args)
+{
+       struct listnode *node;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct sr_prefix *srp;
+       struct sr_nhlfe new;
+
+       /* Process Every Extended Prefix for this SR-Node */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               /* Process Self SRN only if NO-PHP is requested */
+               if ((srn == OspfSR.self)
+                   && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))
+                       continue;
+
+               /* Process only SID Index */
+               if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+                       continue;
+
+               /* OK. Compute new NHLFE */
+               memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+               new.label_in = index2label(srp->sid, OspfSR.srgb);
+               /* Update MPLS LFIB */
+               update_sid_nhlfe(srp->nhlfe, new);
+               /* Finally update Input Label */
+               srp->nhlfe.label_in = new.label_in;
+       }
+}
+
+/*
+ * When SRGB has changed, update NHLFE Output Label for all Extended Prefix
+ * with SID index which use the given SR-Node as nexthop though hash_iterate()
+ */
+static void update_out_nhlfe(struct hash_backet *backet, void *args)
+{
+       struct listnode *node;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct sr_node *srnext = (struct sr_node *)args;
+       struct sr_prefix *srp;
+       struct sr_nhlfe new;
+
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               /* Process only SID Index for next hop without PHP */
+               if ((srp->nexthop == NULL)
+                   && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+                       continue;
+               memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+               new.label_out = index2label(srp->sid, srnext->srgb);
+               update_sid_nhlfe(srp->nhlfe, new);
+               srp->nhlfe.label_out = new.label_out;
+       }
+}
+
+/*
+ * Following functions are call when new Segment Routing LSA are received
+ *  - Router Information: ospf_sr_ri_lsa_update() & ospf_sr_ri_lsa_delete()
+ *  - Extended Link: ospf_sr_ext_link_update() & ospf_sr_ext_link_delete()
+ *  - Extended Prefix: ospf_ext_prefix_update() & ospf_sr_ext_prefix_delete()
+ */
+
+/* Update Segment Routing from Router Information LSA */
+void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct ri_sr_tlv_sid_label_range *ri_srgb;
+       struct ri_sr_tlv_sr_algorithm *algo;
+       struct sr_srgb srgb;
+       uint16_t length = 0, sum = 0;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Process Router "
+                       "Information LSA 4.0.0.%u from %s",
+                       __func__,
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (IS_LSA_SELF(lsa))
+               return;
+
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router),
+                      (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (%s): Abort! can't create SR node in hash table",
+                       __func__);
+               return;
+       }
+
+       if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+               zlog_err(
+                       "SR (%s): Abort! Wrong "
+                       "LSA ID 4.0.0.%u for SR node %s/%u",
+                       __func__,
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router), srn->instance);
+               return;
+       }
+
+       /* Collect Router Information Sub TLVs */
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       srgb.range_size = 0;
+       srgb.lower_bound = 0;
+
+       for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL);
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               switch (ntohs(tlvh->type)) {
+               case RI_SR_TLV_SR_ALGORITHM:
+                       algo = (struct ri_sr_tlv_sr_algorithm *)tlvh;
+                       int i;
+
+                       for (i = 0; i < ntohs(algo->header.length); i++)
+                               srn->algo[i] = algo->value[0];
+                       for (; i < ALGORITHM_COUNT; i++)
+                               srn->algo[i] = SR_ALGORITHM_UNSET;
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               case RI_SR_TLV_SID_LABEL_RANGE:
+                       ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh;
+                       srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size));
+                       srgb.lower_bound =
+                               GET_LABEL(ntohl(ri_srgb->lower.value));
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               case RI_SR_TLV_NODE_MSD:
+                       srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value;
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               default:
+                       sum += TLV_SIZE(tlvh);
+                       break;
+               }
+       }
+
+       /* Check that we collect mandatory parameters */
+       if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0
+           || srgb.lower_bound == 0) {
+               zlog_warn(
+                       "SR (%s): Missing mandatory parameters. Abort!",
+                       __func__);
+               hash_release(OspfSR.neighbors, &(srn->adv_router));
+               XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+               return;
+       }
+
+       /* Check if it is a new SR Node or not */
+       if (srn->instance == 0) {
+               /* update LSA ID */
+               srn->instance = ntohl(lsah->id.s_addr);
+               /* Copy SRGB */
+               srn->srgb.range_size = srgb.range_size;
+               srn->srgb.lower_bound = srgb.lower_bound;
+       }
+
+       /* Check if SRGB has changed */
+       if ((srn->srgb.range_size != srgb.range_size)
+           || (srn->srgb.lower_bound != srgb.lower_bound)) {
+               srn->srgb.range_size = srgb.range_size;
+               srn->srgb.lower_bound = srgb.lower_bound;
+               /* Update NHLFE if it is a neighbor SR node */
+               if (srn->neighbor == OspfSR.self)
+                       hash_iterate(OspfSR.neighbors,
+                                    (void (*)(struct hash_backet *,
+                                              void *))update_out_nhlfe,
+                                    (void *)srn);
+       }
+
+}
+
+/*
+ * Delete SR Node entry in hash table information corresponding to an expired
+ * Router Information LSA
+ */
+void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Remove SR node %s from lsa_id 4.0.0.%u",
+                       __func__, inet_ntoa(lsah->adv_router),
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR Data Base", __func__);
+               return;
+       }
+
+       /* Release Router ID entry in SRDB hash table */
+       srn = hash_release(OspfSR.neighbors, &(lsah->adv_router));
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (%s): Abort! no entry in SRDB for SR Node %s",
+                       __func__, inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+               zlog_err(
+                       "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %s",
+                       __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Remove SR node */
+       sr_node_del(srn);
+
+}
+
+/* Update Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct sr_link *srl;
+
+       uint16_t length, sum;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Process Extended Link LSA 8.0.0.%u from %s",
+                       __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+                                        (void *)&(lsah->adv_router),
+                                        (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (%s): Abort! can't create SR node in hash table",
+                       __func__);
+               return;
+       }
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       sum = 0;
+       for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL);
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+                       /* Got Extended Link information */
+                       srl = get_ext_link_sid(tlvh);
+                       /* Update SID if not null */
+                       if (srl != NULL) {
+                               srl->instance = ntohl(lsah->id.s_addr);
+                               update_ext_link_sid(srn, srl, lsa->flags);
+                       }
+               }
+               sum += TLV_SIZE(tlvh);
+       }
+}
+
+/* Delete Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct sr_link *srl;
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       uint32_t instance = ntohl(lsah->id.s_addr);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Remove Extended Link LSA 8.0.0.%u from %s",
+                       __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+               return;
+       }
+
+       /* Search SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                           (void *)&(lsah->adv_router));
+
+       /*
+        * SR-Node may be NULL if it has been remove previously when
+        * processing Router Information LSA deletion
+        */
+       if (srn == NULL) {
+               zlog_warn(
+                       "SR (%s): Stop! no entry in SRDB for SR Node %s",
+                       __func__, inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Search for corresponding Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl))
+               if (srl->instance == instance)
+                       break;
+
+       /* Remove Segment Link if found */
+       if ((srl != NULL) && (srl->instance == instance)) {
+               del_sid_nhlfe(srl->nhlfe[0]);
+               del_sid_nhlfe(srl->nhlfe[1]);
+               listnode_delete(srn->ext_link, srl);
+               XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+       } else {
+               zlog_warn(
+                       "SR (%s): Didn't found corresponding SR Link 8.0.0.%u "
+                       "for SR Node %s", __func__,
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+       }
+
+}
+
+/* Update Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa)
+{
+       struct sr_node *srn;
+       struct tlv_header *tlvh;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       struct sr_prefix *srp;
+
+       uint16_t length, sum;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Process Extended Prefix LSA "
+                       "7.0.0.%u from %s", __func__,
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+               return;
+       }
+
+       /* Get SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+                                        (void *)&(lsah->adv_router),
+                                        (void *)sr_node_new);
+
+       /* Sanity check */
+       if (srn == NULL) {
+               zlog_err(
+                       "SR (%s): Abort! can't create SR node in hash table",
+                       __func__);
+               return;
+       }
+
+       /* Initialize TLV browsing */
+       length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+       sum = 0;
+       for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+            tlvh = TLV_HDR_NEXT(tlvh)) {
+               if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+                       /* Got Extended Link information */
+                       srp = get_ext_prefix_sid(tlvh);
+                       /* Update SID if not null */
+                       if (srp != NULL) {
+                               srp->instance = ntohl(lsah->id.s_addr);
+                               update_ext_prefix_sid(srn, srp);
+                       }
+               }
+               sum += TLV_SIZE(tlvh);
+       }
+}
+
+/* Delete Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa)
+{
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct sr_node *srn;
+       struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+       uint32_t instance = ntohl(lsah->id.s_addr);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %s",
+                       __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+
+       /* Sanity check */
+       if (OspfSR.neighbors == NULL) {
+               zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+               return;
+       }
+
+       /* Search SR Node in hash table from Router ID */
+       srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                           (void *)&(lsah->adv_router));
+
+       /*
+        * SR-Node may be NULL if it has been remove previously when
+        * processing Router Information LSA deletion
+        */
+       if (srn == NULL) {
+               zlog_warn(
+                       "SR (%s):  Stop! no entry in SRDB for SR Node %s",
+                       __func__, inet_ntoa(lsah->adv_router));
+               return;
+       }
+
+       /* Search for corresponding Segment Link */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
+               if (srp->instance == instance)
+                       break;
+
+       /* Remove Segment Link if found */
+       if ((srp != NULL) && (srp->instance == instance)) {
+               del_sid_nhlfe(srp->nhlfe);
+               listnode_delete(srn->ext_link, srp);
+               XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+       } else {
+               zlog_warn(
+                       "SR (%s): Didn't found corresponding SR Prefix "
+                       "7.0.0.%u for SR Node %s", __func__,
+                       GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+                       inet_ntoa(lsah->adv_router));
+       }
+
+}
+
+/* Get Label for Extended Link SID */
+/* TODO: To be replace by Zebra Label Manager */
+uint32_t get_ext_link_label_value(void)
+{
+       static uint32_t label = ADJ_SID_MIN - 1;
+
+       if (label < ADJ_SID_MAX)
+               label += 1;
+
+       return label;
+}
+
+/*
+ * Update Prefix SID. Call by ospf_ext_pref_ism_change to
+ * complete initial CLI command at startutp.
+ *
+ * @param ifp - Loopback interface
+ * @param pref - Prefix address of this interface
+ *
+ * @return - void
+ */
+void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p)
+{
+       struct listnode *node;
+       struct sr_prefix *srp;
+
+       /* Sanity Check */
+       if ((ifp == NULL) || (p == NULL))
+               return;
+
+       /*
+        * Search if there is a Segment Prefix that correspond to this
+        * interface or prefix, and update it if found
+        */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+               if ((srp->nhlfe.ifindex == ifp->ifindex)
+                   || ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix,
+                   &p->u.prefix4))
+                   && (srp->nhlfe.prefv4.prefixlen == p->prefixlen))) {
+
+                       /* Update Interface & Prefix info */
+                       srp->nhlfe.ifindex = ifp->ifindex;
+                       IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
+                               &p->u.prefix4);
+                       srp->nhlfe.prefv4.prefixlen = p->prefixlen;
+                       srp->nhlfe.prefv4.family = p->family;
+                       IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4);
+
+                       /* OK. Let's Schedule Extended Prefix LSA */
+                       srp->instance = ospf_ext_schedule_prefix_index(ifp,
+                               srp->sid, &srp->nhlfe.prefv4, srp->flags);
+
+                       /* Install NHLFE if NO-PHP is requested */
+                       if (CHECK_FLAG(srp->flags,
+                           EXT_SUBTLV_PREFIX_SID_NPFLG)) {
+                               srp->nhlfe.label_in = index2label(srp->sid,
+                                               OspfSR.self->srgb);
+                               srp->nhlfe.label_out = MPLS_IMP_NULL_LABEL;
+                               add_sid_nhlfe(srp->nhlfe);
+                       }
+               }
+       }
+}
+
+/*
+ * Following functions are used to update MPLS LFIB after a SPF run
+ */
+
+static void ospf_sr_nhlfe_update(struct hash_backet *backet, void *args)
+{
+
+       struct sr_node *srn = (struct sr_node *)backet->data;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct sr_nhlfe old;
+       int rc;
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("  |-  Update Prefix for SR Node %s",
+                          inet_ntoa(srn->adv_router));
+
+       /* Skip Self SR Node */
+       if (srn == OspfSR.self)
+               return;
+
+       /* Update Extended Prefix */
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+
+               /* Backup current NHLFE */
+               memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe));
+
+               /* Compute the new NHLFE */
+               rc = compute_prefix_nhlfe(srp);
+
+               /* Check computation result */
+               switch (rc) {
+               /* next hop is not know, remove old NHLFE to avoid loop */
+               case -1:
+                       del_sid_nhlfe(srp->nhlfe);
+                       break;
+               /* next hop has not changed, skip it */
+               case 0:
+                       break;
+               /* there is a new next hop, update NHLFE */
+               case 1:
+                       update_sid_nhlfe(old, srp->nhlfe);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static int ospf_sr_update_schedule(struct thread *t)
+{
+
+       struct ospf *ospf;
+       struct timeval start_time, stop_time;
+
+       ospf = THREAD_ARG(t);
+       ospf->t_sr_update = NULL;
+
+       if (!OspfSR.update)
+               return 0;
+
+       monotime(&start_time);
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug("SR (%s): Start SPF update", __func__);
+
+       hash_iterate(OspfSR.neighbors, (void (*)(struct hash_backet *,
+                                                void *))ospf_sr_nhlfe_update,
+                    NULL);
+
+       monotime(&stop_time);
+
+       zlog_info(
+               "SR (%s): SPF Processing Time(usecs): %lld\n",
+               __func__,
+               (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+                       + (stop_time.tv_usec - start_time.tv_usec));
+
+       OspfSR.update = false;
+       return 1;
+}
+
+#define OSPF_SR_UPDATE_INTERVAL        1
+
+void ospf_sr_update_timer_add(struct ospf *ospf)
+{
+
+       if (ospf == NULL)
+               return;
+
+       /* Check if an update is not alreday engage */
+       if (OspfSR.update)
+               return;
+
+       OspfSR.update = true;
+
+       thread_add_timer(master, ospf_sr_update_schedule, ospf,
+                        OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update);
+}
+
+/*
+ * --------------------------------------
+ * Followings are vty command functions.
+ * --------------------------------------
+ */
+
+/*
+ * Segment Routing Router configuration
+ *
+ * Must be centralize as it concerns both Extended Link/Prefix LSA
+ * and Router Information LSA. Choose to call it from Extended Prefix
+ * write_config() call back.
+ *
+ * @param vty VTY output
+ *
+ * @return none
+ */
+void ospf_sr_config_write_router(struct vty *vty)
+{
+       struct listnode *node;
+       struct sr_prefix *srp;
+
+       if (OspfSR.enabled) {
+               vty_out(vty, " segment-routing on\n");
+
+               if ((OspfSR.srgb.lower_bound != MPLS_DEFAULT_MIN_SRGB_LABEL)
+                   || (OspfSR.srgb.range_size != MPLS_DEFAULT_MAX_SRGB_SIZE)) {
+                       vty_out(vty, " segment-routing global-block %u %u\n",
+                                       OspfSR.srgb.lower_bound,
+                                       OspfSR.srgb.lower_bound +
+                                       OspfSR.srgb.range_size - 1);
+               }
+               if (OspfSR.msd != 0)
+                       vty_out(vty, " segment-routing node-msd %u\n",
+                               OspfSR.msd);
+
+               if (OspfSR.self != NULL) {
+                       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node,
+                                                 srp)) {
+                               vty_out(vty,
+                                       " segment-routing prefix %s/%u "
+                                       "index %u%s\n",
+                                       inet_ntoa(srp->nhlfe.prefv4.prefix),
+                                       srp->nhlfe.prefv4.prefixlen, srp->sid,
+                                       CHECK_FLAG(srp->flags,
+                                               EXT_SUBTLV_PREFIX_SID_NPFLG) ?
+                                               " no-php-flag" : "");
+                       }
+               }
+       }
+}
+
+DEFUN(ospf_sr_enable,
+       ospf_sr_enable_cmd,
+       "segment-routing on",
+       SR_STR
+       "Enable Segment Routing\n")
+{
+
+       VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+       if (OspfSR.enabled)
+               return CMD_SUCCESS;
+
+       if (ospf->vrf_id != VRF_DEFAULT) {
+               vty_out(vty, "Segment Routing is only supported in default "
+                            "VRF\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Segment Routing: OFF -> ON");
+
+       /* Start Segment Routing */
+       OspfSR.enabled = true;
+       if (!ospf_sr_start(ospf)) {
+               zlog_warn("SR: Unable to start Segment Routing. Abort!");
+               return CMD_WARNING;
+       }
+
+       /* Set Router Information SR parameters */
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Activate SR for Router Information LSA");
+
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update Ext LSA */
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Activate SR for Extended Link/Prefix LSA");
+
+       ospf_ext_update_sr(true);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_sr_enable,
+       no_ospf_sr_enable_cmd,
+       "no segment-routing [on]",
+       NO_STR
+       SR_STR
+       "Disable Segment Routing\n")
+{
+
+       if (!OspfSR.enabled)
+               return CMD_SUCCESS;
+
+       if (IS_DEBUG_OSPF_EVENT)
+               zlog_debug("SR: Segment Routing: ON -> OFF");
+
+       /* Start by Disabling Extended Link & Prefix LSA */
+       ospf_ext_update_sr(false);
+
+       /* then, disable Router Information SR parameters */
+       ospf_router_info_update_sr(false, OspfSR.srgb, OspfSR.msd);
+
+       /* Finally, stop Segment Routing */
+       ospf_sr_stop();
+       OspfSR.enabled = false;
+
+       return CMD_SUCCESS;
+}
+
+static int ospf_sr_enabled(struct vty *vty)
+{
+       if (OspfSR.enabled)
+               return 1;
+
+       if (vty)
+               vty_out(vty, "%% OSPF SR is not turned on\n");
+
+       return 0;
+}
+
+DEFUN (sr_sid_label_range,
+       sr_sid_label_range_cmd,
+       "segment-routing global-block (0-1048575) (0-1048575)",
+       SR_STR
+       "Segment Routing Global Block label range\n"
+       "Lower-bound range in decimal (0-1048575)\n"
+       "Upper-bound range in decimal (0-1048575)\n")
+{
+       uint32_t upper;
+       uint32_t lower;
+       uint32_t size;
+       int idx_low = 2;
+       int idx_up = 3;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Get lower and upper bound */
+       lower = strtoul(argv[idx_low]->arg, NULL, 10);
+       upper = strtoul(argv[idx_up]->arg, NULL, 10);
+       size = upper - lower + 1;
+
+       if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) {
+               vty_out(vty,
+                       "Range size cannot be less than 0 or more than %u\n",
+                       MPLS_DEFAULT_MAX_SRGB_SIZE);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) {
+               vty_out(vty, "Upper-bound cannot exceed %u\n",
+                       MPLS_DEFAULT_MAX_SRGB_LABEL);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) {
+               vty_out(vty, "Upper-bound cannot be lower than %u\n",
+                       MPLS_DEFAULT_MIN_SRGB_LABEL);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Check if values have changed */
+       if ((OspfSR.srgb.range_size == size)
+           && (OspfSR.srgb.lower_bound == lower))
+               return CMD_SUCCESS;
+
+       /* Set SID/Label range SRGB */
+       OspfSR.srgb.range_size = size;
+       OspfSR.srgb.lower_bound = lower;
+       if (OspfSR.self != NULL) {
+               OspfSR.self->srgb.range_size = size;
+               OspfSR.self->srgb.lower_bound = lower;
+       }
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update NHLFE entries */
+       hash_iterate(OspfSR.neighbors,
+                    (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+                    NULL);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_sid_label_range,
+       no_sr_sid_label_range_cmd,
+       "no segment-routing global-block [(0-1048575) (0-1048575)]",
+       NO_STR
+       SR_STR
+       "Segment Routing Global Block label range\n"
+       "Lower-bound range in decimal (0-1048575)\n"
+       "Upper-bound range in decimal (0-1048575)\n")
+{
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Revert to default SRGB value */
+       OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE;
+       OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+       if (OspfSR.self != NULL) {
+               OspfSR.self->srgb.range_size = OspfSR.srgb.range_size;
+               OspfSR.self->srgb.lower_bound = OspfSR.srgb.lower_bound;
+       }
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       /* Update NHLFE entries */
+       hash_iterate(OspfSR.neighbors,
+                    (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+                    NULL);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (sr_node_msd,
+       sr_node_msd_cmd,
+       "segment-routing node-msd (1-16)",
+       SR_STR
+       "Maximum Stack Depth for this router\n"
+       "Maximum number of label that could be stack (1-16)\n")
+{
+       uint32_t msd;
+       int idx = 1;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Get MSD */
+       argv_find(argv, argc, "(1-16)", &idx);
+       msd = strtoul(argv[idx]->arg, NULL, 10);
+       if (msd < 1 || msd > MPLS_MAX_LABELS) {
+               vty_out(vty, "MSD must be comprise between 1 and %u\n",
+                       MPLS_MAX_LABELS);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Check if value has changed */
+       if (OspfSR.msd == msd)
+               return CMD_SUCCESS;
+
+       /* Set this router MSD */
+       OspfSR.msd = msd;
+       if (OspfSR.self != NULL)
+               OspfSR.self->msd = msd;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_node_msd,
+       no_sr_node_msd_cmd,
+       "no segment-routing node-msd [(1-16)]",
+       NO_STR
+       SR_STR
+       "Maximum Stack Depth for this router\n"
+       "Maximum number of label that could be stack (1-16)\n")
+{
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* unset this router MSD */
+       OspfSR.msd = 0;
+       if (OspfSR.self != NULL)
+               OspfSR.self->msd = 0;
+
+       /* Set Router Information SR parameters */
+       ospf_router_info_update_sr(true, OspfSR.srgb, 0);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (sr_prefix_sid,
+       sr_prefix_sid_cmd,
+       "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]",
+       SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix as A.B.C.D/M\n"
+       "SID index for this prefix in decimal (0-65535)\n"
+       "Index value inside SRGB (lower_bound < index < upper_bound)\n"
+       "Don't request Penultimate Hop Popping (PHP)\n")
+{
+       int idx = 0;
+       struct prefix p;
+       uint32_t index;
+       struct listnode *node;
+       struct sr_prefix *srp, *new;
+       struct interface *ifp;
+
+       if (!ospf_sr_enabled(vty))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       /* Get network prefix */
+       argv_find(argv, argc, "A.B.C.D/M", &idx);
+       if (!str2prefix(argv[idx]->arg, &p)) {
+               vty_out(vty, "Invalid prefix format %s\n",
+                       argv[idx]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Get & verify index value */
+       argv_find(argv, argc, "(0-65535)", &idx);
+       index = strtoul(argv[idx]->arg, NULL, 10);
+       if (index > OspfSR.srgb.range_size - 1) {
+               vty_out(vty, "Index %u must be lower than range size %u\n",
+                       index, OspfSR.srgb.range_size);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* check that the index is not already used */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+               if (srp->sid == index) {
+                       vty_out(vty, "Index %u is already used\n", index);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+       }
+
+       /* Create new Extended Prefix to SRDB if not found */
+       new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+       IPV4_ADDR_COPY(&new->nhlfe.prefv4.prefix, &p.u.prefix4);
+       IPV4_ADDR_COPY(&new->nhlfe.nexthop, &p.u.prefix4);
+       new->nhlfe.prefv4.prefixlen = p.prefixlen;
+       new->nhlfe.prefv4.family = p.family;
+       new->sid = index;
+       /* Set NO PHP flag if present and compute NHLFE */
+       if (argv_find(argv, argc, "no-php-flag", &idx)) {
+               SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG);
+               new->nhlfe.label_in = index2label(new->sid, OspfSR.self->srgb);
+               new->nhlfe.label_out = MPLS_IMP_NULL_LABEL;
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Add new index %u to Prefix %s/%u",
+                       __func__, index, inet_ntoa(new->nhlfe.prefv4.prefix),
+                       new->nhlfe.prefv4.prefixlen);
+
+       /* Get Interface and check if it is a Loopback */
+       ifp = if_lookup_prefix(&p, VRF_DEFAULT);
+       if (ifp == NULL) {
+               /*
+                * Interface could be not yet available i.e. when this
+                * command is in the configuration file, OSPF is not yet
+                * ready. In this case, store the prefix SID for latter
+                * update of this Extended Prefix
+                */
+               listnode_add(OspfSR.self->ext_prefix, new);
+               zlog_warn(
+                       "Interface for prefix %s/%u not found. Deferred LSA "
+                       "flooding", inet_ntoa(p.u.prefix4), p.prefixlen);
+               return CMD_SUCCESS;
+       }
+
+       if (!if_is_loopback(ifp)) {
+               vty_out(vty, "interface %s is not a Loopback\n", ifp->name);
+               XFREE(MTYPE_OSPF_SR_PARAMS, new);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       new->nhlfe.ifindex = ifp->ifindex;
+
+       /* Search if this prefix already exist */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+               if ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
+                   && srp->nhlfe.prefv4.prefixlen == p.prefixlen))
+                       break;
+               else
+                       srp = NULL;
+       }
+
+       /* Update or Add this new SR Prefix */
+       if (srp) {
+               update_sid_nhlfe(srp->nhlfe, new->nhlfe);
+               listnode_delete(OspfSR.self->ext_prefix, srp);
+               listnode_add(OspfSR.self->ext_prefix, new);
+       } else {
+               listnode_add(OspfSR.self->ext_prefix, new);
+               add_sid_nhlfe(new->nhlfe);
+       }
+
+       /* Finally, update Extended Prefix LSA */
+       new->instance = ospf_ext_schedule_prefix_index(ifp, new->sid,
+                               &new->nhlfe.prefv4, new->flags);
+       if (new->instance == 0) {
+               vty_out(vty, "Unable to set index %u for prefix %s/%u\n", index,
+                       inet_ntoa(p.u.prefix4), p.prefixlen);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_prefix_sid,
+       no_sr_prefix_sid_cmd,
+       "no segment-routing prefix A.B.C.D/M [index (0-65535) no-php-flag]",
+       NO_STR
+       SR_STR
+       "Prefix SID\n"
+       "IPv4 Prefix as A.B.C.D/M\n"
+       "SID index for this prefix in decimal (0-65535)\n"
+       "Index value inside SRGB (lower_bound < index < upper_bound)\n"
+       "Don't request Penultimate Hop Popping (PHP)\n")
+{
+       int idx = 0;
+       struct prefix p;
+       struct listnode *node;
+       struct sr_prefix *srp;
+       struct interface *ifp;
+       bool found = false;
+       int rc;
+
+       /* Get network prefix */
+       argv_find(argv, argc, "A.B.C.D/M", &idx);
+       rc = str2prefix(argv[idx]->arg, &p);
+       if (!rc) {
+               vty_out(vty, "Invalid prefix format %s\n",
+                       argv[idx]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* check that the prefix is already set */
+       for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp))
+               if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
+                   && (srp->nhlfe.prefv4.prefixlen == p.prefixlen)) {
+                       found = true;
+                       break;
+               }
+
+       if (!found) {
+               vty_out(vty, "Prefix %s is not found. Abort!\n",
+                       argv[idx]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Get Interface */
+       ifp = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+       if (ifp == NULL) {
+               vty_out(vty, "interface for prefix %s not found.\n",
+                       argv[idx]->arg);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* Update Extended Prefix LSA */
+       if (!ospf_ext_schedule_prefix_index(ifp, 0, NULL, 0)) {
+               vty_out(vty, "No corresponding loopback interface. Abort!\n");
+               return CMD_WARNING;
+       }
+
+       if (IS_DEBUG_OSPF_SR)
+               zlog_debug(
+                       "SR (%s): Remove Prefix %s/%u with index %u",
+                       __func__, inet_ntoa(srp->nhlfe.prefv4.prefix),
+                       srp->nhlfe.prefv4.prefixlen, srp->sid);
+
+       /* Delete NHLFE is NO-PHP is set */
+       if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))
+               del_sid_nhlfe(srp->nhlfe);
+
+       /* OK, all is clean, remove SRP from SRDB */
+       listnode_delete(OspfSR.self->ext_prefix, srp);
+       XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+
+       return CMD_SUCCESS;
+}
+
+
+
+static void show_vty_sr_node(struct vty *vty, struct sr_node *srn)
+{
+
+       struct listnode *node;
+       struct sr_link *srl;
+       struct sr_prefix *srp;
+       struct interface *itf;
+       char pref[16];
+       char sid[22];
+       char label[8];
+
+       /* Sanity Check */
+       if (srn == NULL)
+               return;
+
+       vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
+       vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size,
+               srn->srgb.lower_bound);
+       vty_out(vty, "\tAlgorithm(s): %s",
+               srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+       for (int i = 1; i < ALGORITHM_COUNT; i++) {
+               if (srn->algo[i] == SR_ALGORITHM_UNSET)
+                       continue;
+               vty_out(vty, "/%s",
+                       srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+       }
+       if (srn->msd != 0)
+               vty_out(vty, "\tMSD: %u", srn->msd);
+
+       vty_out(vty,
+               "\n\n    Prefix or Link  Label In  Label Out       "
+               "Node or Adj. SID  Interface          Nexthop\n");
+       vty_out(vty,
+               "------------------  --------  ---------  "
+               "---------------------  ---------  ---------------\n");
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+               strncpy(pref, inet_ntoa(srp->nhlfe.prefv4.prefix), 16);
+               snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid);
+               if (srp->nhlfe.label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%u", srp->nhlfe.label_out);
+               itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+               vty_out(vty, "%15s/%u  %8u  %9s  %21s  %9s  %15s\n", pref,
+                       srp->nhlfe.prefv4.prefixlen, srp->nhlfe.label_in, label,
+                       sid, itf ? itf->name : "-",
+                       inet_ntoa(srp->nhlfe.nexthop));
+       }
+
+       for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
+               strncpy(pref, inet_ntoa(srl->nhlfe[0].prefv4.prefix), 16);
+               snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]);
+               if (srl->nhlfe[0].label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%u", srl->nhlfe[0].label_out);
+               itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
+               vty_out(vty, "%15s/%u  %8u  %9s  %21s  %9s  %15s\n", pref,
+                       srl->nhlfe[0].prefv4.prefixlen, srl->nhlfe[0].label_in,
+                       label, sid, itf ? itf->name : "-",
+                       inet_ntoa(srl->nhlfe[0].nexthop));
+               snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
+               if (srl->nhlfe[1].label_out == MPLS_IMP_NULL_LABEL)
+                       sprintf(label, "pop");
+               else
+                       sprintf(label, "%u", srl->nhlfe[0].label_out);
+               vty_out(vty, "%15s/%u  %8u  %9s  %21s  %9s  %15s\n", pref,
+                       srl->nhlfe[1].prefv4.prefixlen, srl->nhlfe[1].label_in,
+                       label, sid, itf ? itf->name : "-",
+                       inet_ntoa(srl->nhlfe[1].nexthop));
+       }
+       vty_out(vty, "\n");
+}
+
+static void show_srdb_entry(struct hash_backet *backet, void *args)
+{
+       struct vty *vty = (struct vty *)args;
+       struct sr_node *srn = (struct sr_node *)backet->data;
+
+       show_vty_sr_node(vty, srn);
+}
+
+DEFUN (show_ip_opsf_srdb,
+       show_ip_ospf_srdb_cmd,
+       "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate]",
+       SHOW_STR
+       IP_STR
+       OSPF_STR
+       "Database summary\n"
+       "Show Segment Routing Data Base\n"
+       "Advertising SR node\n"
+       "Advertising SR node ID (as an IP address)\n"
+       "Self-originated SR node\n")
+{
+       int idx = 0;
+       struct in_addr rid;
+       struct sr_node *srn;
+
+       if (!OspfSR.enabled) {
+               vty_out(vty, "Segment Routing is disabled on this router\n");
+               return CMD_WARNING;
+       }
+
+       vty_out(vty, "\n          OSPF Segment Routing database for ID %s\n\n",
+               inet_ntoa(OspfSR.self->adv_router));
+
+       if (argv_find(argv, argc, "self-originate", &idx)) {
+               srn = OspfSR.self;
+               show_vty_sr_node(vty, srn);
+               return CMD_SUCCESS;
+       }
+
+       if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+               if (!inet_aton(argv[idx]->arg, &rid)) {
+                       vty_out(vty,
+                               "Specified Router ID %s is invalid\n",
+                               argv[idx]->arg);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               /* Get the SR Node from the SRDB */
+               srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+                                                   (void *)&rid);
+               show_vty_sr_node(vty, srn);
+               return CMD_SUCCESS;
+       }
+
+       /* No parameters have been provided, Iterate through all the SRDB */
+       hash_iterate(
+               OspfSR.neighbors,
+               (void (*)(struct hash_backet *, void *))show_srdb_entry,
+               (void *)vty);
+       return CMD_SUCCESS;
+}
+
+/* Install new CLI commands */
+void ospf_sr_register_vty(void)
+{
+       install_element(VIEW_NODE, &show_ip_ospf_srdb_cmd);
+
+       install_element(OSPF_NODE, &ospf_sr_enable_cmd);
+       install_element(OSPF_NODE, &no_ospf_sr_enable_cmd);
+       install_element(OSPF_NODE, &sr_sid_label_range_cmd);
+       install_element(OSPF_NODE, &no_sr_sid_label_range_cmd);
+       install_element(OSPF_NODE, &sr_node_msd_cmd);
+       install_element(OSPF_NODE, &no_sr_node_msd_cmd);
+       install_element(OSPF_NODE, &sr_prefix_sid_cmd);
+       install_element(OSPF_NODE, &no_sr_prefix_sid_cmd);
+
+}
diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h
new file mode 100644 (file)
index 0000000..cb7d083
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * This is an implementation of Segment Routing
+ * as per draft draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing header definitions
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_OSPF_SR_H
+#define _FRR_OSPF_SR_H
+
+/* Default Route priority for OSPF Segment Routing */
+#define OSPF_SR_PRIORITY_DEFAULT       10
+
+/* macros and constants for segment routing */
+#define SET_RANGE_SIZE_MASK             0xffffff00
+#define GET_RANGE_SIZE_MASK             0x00ffffff
+#define SET_LABEL_MASK                  0xffffff00
+#define GET_LABEL_MASK                  0x00ffffff
+#define SET_RANGE_SIZE(range_size) ((range_size << 8) & SET_RANGE_SIZE_MASK)
+#define GET_RANGE_SIZE(range_size) ((range_size >> 8) & GET_RANGE_SIZE_MASK)
+#define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK)
+#define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK)
+
+/* Label range for Adj-SID attribution purpose. Start just right after SRGB */
+#define ADJ_SID_MIN                     MPLS_DEFAULT_MAX_SRGB_LABEL
+#define ADJ_SID_MAX                     (MPLS_DEFAULT_MAX_SRGB_LABEL + 1000)
+
+#define OSPF_SR_DEFAULT_METRIC         1
+
+/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */
+
+/* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */
+#define SID_BASE_SIZE  4
+#define SID_LABEL      3
+#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL)
+#define SID_INDEX      4
+#define SID_INDEX_SIZE (SID_BASE_SIZE + SID_INDEX)
+
+/* SID/Label Sub TLV - section 2.1 */
+#define SUBTLV_SID_LABEL               1
+#define SUBTLV_SID_LABEL_SIZE          8
+struct subtlv_sid_label {
+       /* Length is 3 (20 rightmost bits MPLS label) or 4 (32 bits SID) */
+       struct tlv_header header;
+       uint32_t value;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Router Information Opaque LSA.
+ */
+
+/* RI SR-Algorithm TLV - section 3.1 */
+#define RI_SR_TLV_SR_ALGORITHM          8
+struct ri_sr_tlv_sr_algorithm {
+       struct tlv_header header;
+#define SR_ALGORITHM_SPF         0
+#define SR_ALGORITHM_STRICT_SPF  1
+#define SR_ALGORITHM_UNSET       255
+#define ALGORITHM_COUNT          4
+       /* Only 4 algorithms supported in this code */
+       uint8_t value[ALGORITHM_COUNT];
+};
+
+/* RI SID/Label Range TLV - section 3.2 */
+#define RI_SR_TLV_SID_LABEL_RANGE      9
+struct ri_sr_tlv_sid_label_range {
+       struct tlv_header header;
+/* Only 24 upper most bits are significant */
+#define SID_RANGE_LABEL_LENGTH 3
+       uint32_t size;
+       /* A SID/Label sub-TLV will follow. */
+       struct subtlv_sid_label lower;
+};
+
+/* RI Node/MSD TLV as per draft-ietf-ospf-segment-routing-msd-05 */
+#define RI_SR_TLV_NODE_MSD             12
+struct ri_sr_tlv_node_msd {
+       struct tlv_header header;
+       uint8_t subtype; /* always = 1 */
+       uint8_t value;
+       uint16_t padding;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Adj-SID and LAN-Ajd-SID subtlvs' flags */
+#define EXT_SUBTLV_LINK_ADJ_SID_BFLG   0x80
+#define EXT_SUBTLV_LINK_ADJ_SID_VFLG   0x40
+#define EXT_SUBTLV_LINK_ADJ_SID_LFLG   0x20
+#define EXT_SUBTLV_LINK_ADJ_SID_SFLG   0x10
+
+/* Prefix SID subtlv Flags */
+#define EXT_SUBTLV_PREFIX_SID_NPFLG    0x40
+#define EXT_SUBTLV_PREFIX_SID_MFLG     0x20
+#define EXT_SUBTLV_PREFIX_SID_EFLG     0x10
+#define EXT_SUBTLV_PREFIX_SID_VFLG     0x08
+#define EXT_SUBTLV_PREFIX_SID_LFLG     0x04
+
+/* SID/Label Binding subtlv Flags */
+#define EXT_SUBTLV_SID_BINDING_MFLG    0x80
+
+/* Extended Prefix Range TLV - section 4 */
+#define EXT_TLV_PREF_RANGE             2
+#define EXT_SUBTLV_PREFIX_RANGE_SIZE   12
+struct ext_tlv_prefix_range {
+       struct tlv_header header;
+       uint8_t pref_length;
+       uint8_t af;
+       uint16_t range_size;
+       uint8_t flags;
+       uint8_t reserved[3];
+       struct in_addr address;
+};
+
+/* Prefix SID Sub-TLV - section 5 */
+#define EXT_SUBTLV_PREFIX_SID          2
+#define EXT_SUBTLV_PREFIX_SID_SIZE     8
+struct ext_subtlv_prefix_sid {
+       struct tlv_header header;
+       uint8_t flags;
+       uint8_t reserved;
+       uint8_t mtid;
+       uint8_t algorithm;
+       uint32_t value;
+};
+
+/* Adj-SID Sub-TLV - section 6.1 */
+#define EXT_SUBTLV_ADJ_SID             2
+#define EXT_SUBTLV_ADJ_SID_SIZE                8
+struct ext_subtlv_adj_sid {
+       struct tlv_header header;
+       uint8_t flags;
+       uint8_t reserved;
+       uint8_t mtid;
+       uint8_t weight;
+       uint32_t value;
+};
+
+/* LAN Adj-SID Sub-TLV - section 6.2 */
+#define EXT_SUBTLV_LAN_ADJ_SID         3
+#define EXT_SUBTLV_LAN_ADJ_SID_SIZE    12
+struct ext_subtlv_lan_adj_sid {
+       struct tlv_header header;
+       uint8_t flags;
+       uint8_t reserved;
+       uint8_t mtid;
+       uint8_t weight;
+       struct in_addr neighbor_id;
+       uint32_t value;
+};
+
+/*
+ * Following section define structure used to manage Segment Routing
+ * information and TLVs / SubTLVs
+ */
+
+/* Structure aggregating SRGB info retrieved from an lsa */
+struct sr_srgb {
+       uint32_t range_size;
+       uint32_t lower_bound;
+};
+
+/* SID type to make difference between loopback interfaces and others */
+enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID };
+
+/* Structure aggregating all OSPF Segment Routing information for the node */
+struct ospf_sr_db {
+       /* Status of Segment Routing: enable or disable */
+       bool enabled;
+
+       /* Ongoing Update following an OSPF SPF */
+       bool update;
+
+       /* Flooding Scope: Area = 10 or AS = 11 */
+       uint8_t scope;
+
+       /* FRR SR node */
+       struct sr_node *self;
+
+       /* List of neighbour SR nodes */
+       struct hash *neighbors;
+
+       /* List of SR prefix */
+       struct route_table *prefix;
+
+       /* Local SR info announced in Router Info LSA */
+
+       /* Algorithms supported by the node */
+       uint8_t algo[ALGORITHM_COUNT];
+       /*
+        * Segment Routing Global Block i.e. label range
+        * Only one range supported in this code
+        */
+       struct sr_srgb srgb;
+       /* Maximum SID Depth supported by the node */
+       uint8_t msd;
+};
+
+/* Structure aggregating all received SR info from LSAs by node */
+struct sr_node {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       uint32_t instance;
+
+       uint8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */
+       /* Segment Routing Global Block i.e. label range */
+       struct sr_srgb srgb;
+       uint8_t msd; /* Maximum SID Depth */
+
+       /* List of Prefix & Link advertise by this node */
+       struct list *ext_prefix; /* For Node SID */
+       struct list *ext_link;   /* For Adj and LAN SID */
+
+       /* Pointer to FRR SR-Node or NULL if it is not a neighbor */
+       struct sr_node *neighbor;
+};
+
+
+/* Segment Routing - NHLFE info: support IPv4 Only */
+struct sr_nhlfe {
+       struct prefix_ipv4 prefv4;
+       struct in_addr nexthop;
+       ifindex_t ifindex;
+       mpls_label_t label_in;
+       mpls_label_t label_out;
+};
+
+/* Structure aggregating all Segment Routing Link information */
+/* Link are generally advertised by pair: primary + backup */
+struct sr_link {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       uint32_t instance;
+
+       /* Flags to manage this link parameters. */
+       uint8_t flags[2];
+
+       /* Segment Routing ID */
+       uint32_t sid[2];
+       enum sid_type type;
+
+       /* SR NHLFE for this link */
+       struct sr_nhlfe nhlfe[2];
+
+       /* Back pointer to SR Node which advertise this Link */
+       struct sr_node *srn;
+};
+
+/* Structure aggregating all Segment Routing Prefix information */
+struct sr_prefix {
+       struct in_addr adv_router; /* used to identify sender of LSA */
+       /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+       uint32_t instance;
+
+       /* Flags to manage this prefix parameters. */
+       uint8_t flags;
+
+       /* Segment Routing ID */
+       uint32_t sid;
+       enum sid_type type;
+
+       /* SR NHLFE for this prefix */
+       struct sr_nhlfe nhlfe;
+
+       /* Back pointer to SR Node which advertise this Prefix */
+       struct sr_node *srn;
+
+       /*
+        * Pointer to SR Node which is the next hop for this Prefix
+        * or NULL if next hop is the destination of the prefix
+        */
+       struct sr_node *nexthop;
+};
+
+/* Prototypes definition */
+/* Segment Routing initialisation functions */
+extern int ospf_sr_init(void);
+extern void ospf_sr_term(void);
+extern void ospf_sr_finish(void);
+/* Segment Routing LSA update & delete functions */
+extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa);
+/* Segment Routing configuration functions */
+extern uint32_t get_ext_link_label_value(void);
+extern void ospf_sr_config_write_router(struct vty *vty);
+extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p);
+/* Segment Routing re-routing function */
+extern void ospf_sr_update_timer_add(struct ospf *ospf);
+#endif /* _FRR_OSPF_SR_H */
index 253b272df67f266367d7bded1de589ba9a6bfb31..ab395207b99406766695a6dcb9326506615d785c 100644 (file)
@@ -170,6 +170,7 @@ void ospf_mpls_te_term(void)
 
        ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
                                   OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
+
        OspfMplsTE.enabled = false;
 
        ospf_mpls_te_unregister();
@@ -178,6 +179,14 @@ void ospf_mpls_te_term(void)
        return;
 }
 
+void ospf_mpls_te_finish(void)
+{
+       // list_delete_all_node(OspfMplsTE.iflist);
+
+       OspfMplsTE.enabled = false;
+       OspfMplsTE.inter_as = Off;
+}
+
 /*------------------------------------------------------------------------*
  * Followings are control functions for MPLS-TE parameters management.
  *------------------------------------------------------------------------*/
index 013421451057691235b2a25887b8ce44500ae19d..ed71e54f5404c79b26a0d27e4bf16b1c6f040628 100644 (file)
@@ -406,6 +406,7 @@ struct mpls_te_link {
 /* Prototypes. */
 extern int ospf_mpls_te_init(void);
 extern void ospf_mpls_te_term(void);
+extern void ospf_mpls_te_finish(void);
 extern struct ospf_mpls_te *get_ospf_mpls_te(void);
 extern void ospf_mpls_te_update_if(struct interface *);
 extern void ospf_mpls_te_lsa_schedule(struct mpls_te_link *, enum lsa_opcode);
index 6d28c2cdc5d39b78571931b3d6c968977a125438..1276f5477c815f606a0ec26d5a80286b6542c6aa 100644 (file)
@@ -4300,13 +4300,15 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
 {
        struct ospf_interface *oi;
        struct listnode *node;
-       json_object *json_vrf = NULL;
+       json_object *json_vrf = NULL, *json_nbr_array = NULL;
+       json_object *json_nbr_sub = NULL;
 
        if (use_json) {
                if (use_vrf)
                        json_vrf = json_object_new_object();
                else
                        json_vrf = json;
+               json_nbr_array = json_object_new_array();
        }
 
        if (ospf->instance) {
@@ -4320,9 +4322,19 @@ static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf,
        ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
        if (!use_json)
                show_ip_ospf_neighbour_header(vty);
+       else
+               json_object_object_add(json_vrf, "neighbors",
+                                      json_nbr_array);
 
-       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
-               show_ip_ospf_neighbor_sub(vty, oi, json_vrf, use_json);
+       for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
+               if (ospf_interface_neighbor_count(oi) == 0)
+                       continue;
+               if (use_json) {
+                       json_nbr_sub = json_object_new_object();
+                       json_object_array_add(json_nbr_array, json_nbr_sub);
+               }
+               show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
+       }
 
        if (use_json) {
                if (use_vrf) {
@@ -4698,7 +4710,6 @@ static int show_ip_ospf_neighbor_int_common(struct vty *vty, struct ospf *ospf,
 
        ospf_show_vrf_name(ospf, vty, json, use_vrf);
 
-       /*ifp = if_lookup_by_name(argv[arg_base]->arg, ospf->vrf_id);*/
        ifp = if_lookup_by_name_all_vrf(argv[arg_base]->arg);
        if (!ifp) {
                if (use_json)
@@ -8116,7 +8127,7 @@ DEFUN (no_ip_ospf_area,
 
 DEFUN (ospf_redistribute_source,
        ospf_redistribute_source_cmd,
-       "redistribute " FRR_REDIST_STR_OSPFD " [<metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+       "redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
        REDIST_STR
        FRR_REDIST_HELP_STR_OSPFD
        "Metric for redistributed routes\n"
@@ -8149,13 +8160,15 @@ DEFUN (ospf_redistribute_source,
                if (!str2metric(argv[idx]->arg, &metric))
                        return CMD_WARNING_CONFIG_FAILED;
        }
+       idx = 1;
        /* Get metric type. */
-       else if (argv_find(argv, argc, "(1-2)", &idx)) {
+       if (argv_find(argv, argc, "(1-2)", &idx)) {
                if (!str2metric_type(argv[idx]->arg, &type))
                        return CMD_WARNING_CONFIG_FAILED;
        }
+       idx = 1;
        /* Get route-map */
-       else if (argv_find(argv, argc, "WORD", &idx)) {
+       if (argv_find(argv, argc, "WORD", &idx)) {
                ospf_routemap_set(red, argv[idx]->arg);
        } else
                ospf_routemap_unset(red);
@@ -8165,7 +8178,7 @@ DEFUN (ospf_redistribute_source,
 
 DEFUN (no_ospf_redistribute_source,
        no_ospf_redistribute_source_cmd,
-       "no redistribute " FRR_REDIST_STR_OSPFD " [<metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+       "no redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
        NO_STR
        REDIST_STR
        FRR_REDIST_HELP_STR_OSPFD
@@ -8355,7 +8368,7 @@ DEFUN (no_ospf_distribute_list_out,
 /* Default information originate. */
 DEFUN (ospf_default_information_originate,
        ospf_default_information_originate_cmd,
-       "default-information originate [<always|metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+       "default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
        "Control distribution of default information\n"
        "Distribute a default route\n"
        "Always advertise default route\n"
@@ -8378,18 +8391,21 @@ DEFUN (ospf_default_information_originate,
        /* Check whether "always" was specified */
        if (argv_find(argv, argc, "always", &idx))
                default_originate = DEFAULT_ORIGINATE_ALWAYS;
+       idx = 1;
        /* Get metric value */
-       else if (argv_find(argv, argc, "(0-16777214)", &idx)) {
+       if (argv_find(argv, argc, "(0-16777214)", &idx)) {
                if (!str2metric(argv[idx]->arg, &metric))
                        return CMD_WARNING_CONFIG_FAILED;
        }
+       idx = 1;
        /* Get metric type. */
-       else if (argv_find(argv, argc, "(1-2)", &idx)) {
+       if (argv_find(argv, argc, "(1-2)", &idx)) {
                if (!str2metric_type(argv[idx]->arg, &type))
                        return CMD_WARNING_CONFIG_FAILED;
        }
+       idx = 1;
        /* Get route-map */
-       else if (argv_find(argv, argc, "WORD", &idx))
+       if (argv_find(argv, argc, "WORD", &idx))
                ospf_routemap_set(red, argv[idx]->arg);
        else
                ospf_routemap_unset(red);
@@ -8400,7 +8416,7 @@ DEFUN (ospf_default_information_originate,
 
 DEFUN (no_ospf_default_information_originate,
        no_ospf_default_information_originate_cmd,
-       "no default-information originate [<always|metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+       "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
        NO_STR
        "Control distribution of default information\n"
        "Distribute a default route\n"
@@ -10220,6 +10236,7 @@ static int config_write_ospf_distribute(struct vty *vty, struct ospf *ospf)
                                if (red->dmetric.value >= 0)
                                        vty_out(vty, " metric %d",
                                                red->dmetric.value);
+
                                if (red->dmetric.type == EXTERNAL_METRIC_TYPE_1)
                                        vty_out(vty, " metric-type 1");
 
index 6d583e9b4ac4e486cfd6468dfc2f0390521b2084..86a3293d7110bc4987e5ba0fc3438814eeaf2924 100644 (file)
@@ -32,6 +32,7 @@
 #include "log.h"
 #include "sockunion.h" /* for inet_aton () */
 #include "zclient.h"
+#include "routemap.h"
 #include "plist.h"
 #include "sockopt.h"
 #include "bfd.h"
@@ -554,6 +555,20 @@ void ospf_terminate(void)
        for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf))
                ospf_finish(ospf);
 
+       /* Cleanup route maps */
+       route_map_add_hook(NULL);
+       route_map_delete_hook(NULL);
+       route_map_event_hook(NULL);
+       route_map_finish();
+
+       /* reverse prefix_list_init */
+       prefix_list_add_hook(NULL);
+       prefix_list_delete_hook(NULL);
+       prefix_list_reset();
+
+       /* Cleanup vrf info */
+       ospf_vrf_terminate();
+
        /* Deliberately go back up, hopefully to thread scheduler, as
         * One or more ospf_finish()'s may have deferred shutdown to a timer
         * thread
@@ -595,6 +610,8 @@ static void ospf_finish_final(struct ospf *ospf)
 
        ospf_opaque_type11_lsa_term(ospf);
 
+       ospf_opaque_finish();
+
        ospf_flush_self_originated_lsas_now(ospf);
 
        /* Unregister redistribution */
@@ -844,7 +861,6 @@ static void ospf_area_free(struct ospf_area *area)
        LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
                ospf_discard_from_db(area->ospf, area->lsdb, lsa);
 
-       ospf_opaque_type10_lsa_term(area);
        ospf_lsdb_delete_all(area->lsdb);
        ospf_lsdb_free(area->lsdb);
 
index 5cb8ca85bc9acf93b0493fef9104e2700dc45aff..6954660e0247f11d589de44626396b2777103919 100644 (file)
@@ -240,6 +240,7 @@ struct ospf {
        struct thread *t_external_lsa;      /* AS-external-LSA origin timer. */
        struct thread
                *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */
+       struct thread *t_sr_update; /* Segment Routing update timer */
 
        unsigned int maxage_delay;      /* Delay on Maxage remover timer, sec */
        struct thread *t_maxage;        /* MaxAge LSA remover timer. */
index e063415fbdfa6f03ebf4f55ff9b1f7ba2ef3978f..9f04260366a1598d20b553e181e5f0b01272e50c 100644 (file)
@@ -20,6 +20,7 @@ ospfd_libfrrospf_a_SOURCES = \
        ospfd/ospf_bfd.c \
        ospfd/ospf_dump.c \
        ospfd/ospf_dump_api.c \
+       ospfd/ospf_ext.c \
        ospfd/ospf_flood.c \
        ospfd/ospf_ia.c \
        ospfd/ospf_interface.c \
@@ -36,6 +37,7 @@ ospfd_libfrrospf_a_SOURCES = \
        ospfd/ospf_route.c \
        ospfd/ospf_routemap.c \
        ospfd/ospf_spf.c \
+       ospfd/ospf_sr.c \
        ospfd/ospf_te.c \
        ospfd/ospf_vty.c \
        ospfd/ospf_zebra.c \
@@ -66,6 +68,7 @@ noinst_HEADERS += \
        ospfd/ospf_apiserver.h \
        ospfd/ospf_ase.h \
        ospfd/ospf_bfd.h \
+       ospfd/ospf_ext.h \
        ospfd/ospf_flood.h \
        ospfd/ospf_ia.h \
        ospfd/ospf_interface.h \
@@ -76,6 +79,7 @@ noinst_HEADERS += \
        ospfd/ospf_ri.h \
        ospfd/ospf_route.h \
        ospfd/ospf_spf.h \
+       ospfd/ospf_sr.h \
        ospfd/ospf_te.h \
        ospfd/ospf_vty.h \
        ospfd/ospf_zebra.h \
index 76ba505ad4c15b85d0be965a14334a3d7f30f18c..11aeeddf93ae96e96eb72d6883f75ffad51d3e31 100644 (file)
 #include "pim_bfd.h"
 #include "bfd.h"
 
-static struct cmd_node pim_global_node = {
-       PIM_NODE, "", 1 /* vtysh ? yes */
-};
-
 static struct cmd_node interface_node = {
        INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
 };
@@ -8523,7 +8519,6 @@ DEFUN (show_ip_msdp_sa_sg_vrf_all,
 
 void pim_cmd_init(void)
 {
-       install_node(&pim_global_node, pim_global_config_write); /* PIM_NODE */
        install_node(&interface_node,
                     pim_interface_config_write); /* INTERFACE_NODE */
        if_cmd_init();
index 8da610a3a6d54e0650603e9a3e45fa05352fd5d6..9b7ef2e073a14e06b4849239b4495764e4f54110 100644 (file)
@@ -198,12 +198,13 @@ static int pim_vrf_config_write(struct vty *vty)
                if (!pim)
                        continue;
 
-               if (vrf->vrf_id == VRF_DEFAULT)
-                       continue;
+               if (vrf->vrf_id != VRF_DEFAULT)
+                       vty_frame(vty, "vrf %s\n", vrf->name);
 
-               vty_frame(vty, "vrf %s\n", vrf->name);
                pim_global_config_write_worker(pim, vty);
-               vty_endframe(vty, "!\n");
+
+               if (vrf->vrf_id != VRF_DEFAULT)
+                       vty_endframe(vty, "!\n");
        }
 
        return 0;
index 089639c77e725126e25c7cfa8df24627a37ba5c9..5b297253b2fbb2ce73639625f0136e96937e058d 100644 (file)
 void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
                           struct pim_nexthop_cache *pnc, int command)
 {
-       struct stream *s;
        struct prefix *p;
        int ret;
 
-       /* Check socket. */
-       if (!zclient || zclient->sock < 0)
-               return;
-
        p = &(pnc->rpf.rpf_addr);
-       s = zclient->obuf;
-       stream_reset(s);
-       zclient_create_header(s, command, pim->vrf_id);
-       /* get update for all routes for a prefix */
-       stream_putc(s, 0);
-
-       stream_putw(s, PREFIX_FAMILY(p));
-       stream_putc(s, p->prefixlen);
-       switch (PREFIX_FAMILY(p)) {
-       case AF_INET:
-               stream_put_in_addr(s, &p->u.prefix4);
-               break;
-       case AF_INET6:
-               stream_put(s, &(p->u.prefix6), 16);
-               break;
-       default:
-               break;
-       }
-       stream_putw_at(s, 0, stream_get_endp(s));
-
-       ret = zclient_send_message(zclient);
+       ret = zclient_send_rnh(zclient, command, p, false, pim->vrf_id);
        if (ret < 0)
                zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
 
-
        if (PIM_DEBUG_PIM_NHT) {
                char buf[PREFIX2STR_BUFFER];
                prefix2str(p, buf, sizeof(buf));
@@ -634,13 +608,9 @@ int pim_ecmp_nexthop_search(struct pim_instance *pim,
 int pim_parse_nexthop_update(int command, struct zclient *zclient,
                             zebra_size_t length, vrf_id_t vrf_id)
 {
-       struct stream *s;
-       struct prefix p;
        struct nexthop *nexthop;
        struct nexthop *nhlist_head = NULL;
        struct nexthop *nhlist_tail = NULL;
-       uint32_t metric, distance;
-       u_char nexthop_num = 0;
        int i;
        struct pim_rpf rpf;
        struct pim_nexthop_cache *pnc = NULL;
@@ -649,30 +619,21 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
        struct interface *ifp1 = NULL;
        struct vrf *vrf = vrf_lookup_by_id(vrf_id);
        struct pim_instance *pim;
+       struct zapi_route nhr;
 
        if (!vrf)
                return 0;
        pim = vrf->info;
 
-       s = zclient->ibuf;
-       memset(&p, 0, sizeof(struct prefix));
-       p.family = stream_getw(s);
-       p.prefixlen = stream_getc(s);
-       switch (p.family) {
-       case AF_INET:
-               p.u.prefix4.s_addr = stream_get_ipv4(s);
-               break;
-       case AF_INET6:
-               stream_get(&p.u.prefix6, s, 16);
-               break;
-       default:
-               break;
+       if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+               if (PIM_DEBUG_PIM_NHT)
+                       zlog_debug("%s: Decode of nexthop update from zebra failed",
+                                  __PRETTY_FUNCTION__);
+               return 0;
        }
 
        if (command == ZEBRA_NEXTHOP_UPDATE) {
-               rpf.rpf_addr.family = p.family;
-               rpf.rpf_addr.prefixlen = p.prefixlen;
-               rpf.rpf_addr.u.prefix4.s_addr = p.u.prefix4.s_addr;
+               prefix_copy(&rpf.rpf_addr, &nhr.prefix);
                pnc = pim_nexthop_cache_find(pim, &rpf);
                if (!pnc) {
                        if (PIM_DEBUG_PIM_NHT) {
@@ -692,34 +653,20 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
        }
 
        pnc->last_update = pim_time_monotonic_usec();
-       distance = stream_getc(s);
-       metric = stream_getl(s);
-       nexthop_num = stream_getc(s);
 
-       if (nexthop_num) {
+       if (nhr.nexthop_num) {
                pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
 
-               for (i = 0; i < nexthop_num; i++) {
-                       nexthop = nexthop_new();
-                       nexthop->type = stream_getc(s);
+               for (i = 0; i < nhr.nexthop_num; i++) {
+                       nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
                        switch (nexthop->type) {
                        case NEXTHOP_TYPE_IPV4:
-                               nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
                        case NEXTHOP_TYPE_IFINDEX:
-                               nexthop->ifindex = stream_getl(s);
-                               break;
                        case NEXTHOP_TYPE_IPV4_IFINDEX:
-                               nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
-                               nexthop->ifindex = stream_getl(s);
-                               break;
                        case NEXTHOP_TYPE_IPV6:
-                               stream_get(&nexthop->gate.ipv6, s, 16);
+                       case NEXTHOP_TYPE_BLACKHOLE:
                                break;
                        case NEXTHOP_TYPE_IPV6_IFINDEX:
-                               stream_get(&nexthop->gate.ipv6, s, 16);
-                               nexthop->ifindex = stream_getl(s);
                                ifp1 = if_lookup_by_index(nexthop->ifindex,
                                                          pim->vrf_id);
                                nbr = pim_neighbor_find_if(ifp1);
@@ -733,9 +680,6 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
                                                PIM_NET_INADDR_ANY;
                                }
 
-                               break;
-                       default:
-                               /* do nothing */
                                break;
                        }
 
@@ -757,19 +701,21 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
 
                        if (PIM_DEBUG_PIM_NHT) {
                                char p_str[PREFIX2STR_BUFFER];
-                               prefix2str(&p, p_str, sizeof(p_str));
+
+                               prefix2str(&nhr.prefix, p_str, sizeof(p_str));
                                zlog_debug(
                                        "%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ",
                                        __PRETTY_FUNCTION__, p_str,
                                        pim->vrf->name, i + 1,
                                        inet_ntoa(nexthop->gate.ipv4),
-                                       ifp->name, nexthop->type, distance,
-                                       metric);
+                                       ifp->name, nexthop->type, nhr.distance,
+                                       nhr.metric);
                        }
 
                        if (!ifp->info) {
                                if (PIM_DEBUG_PIM_NHT) {
                                        char buf[NEXTHOP_STRLEN];
+
                                        zlog_debug(
                                                "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
                                                __PRETTY_FUNCTION__, ifp->name,
@@ -797,23 +743,24 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient,
                pnc->nexthop = nhlist_head;
                if (pnc->nexthop_num) {
                        pnc->flags |= PIM_NEXTHOP_VALID;
-                       pnc->distance = distance;
-                       pnc->metric = metric;
+                       pnc->distance = nhr.distance;
+                       pnc->metric = nhr.metric;
                }
        } else {
                pnc->flags &= ~PIM_NEXTHOP_VALID;
-               pnc->nexthop_num = nexthop_num;
+               pnc->nexthop_num = nhr.nexthop_num;
                nexthops_free(pnc->nexthop);
                pnc->nexthop = NULL;
        }
 
        if (PIM_DEBUG_PIM_NHT) {
                char buf[PREFIX2STR_BUFFER];
-               prefix2str(&p, buf, sizeof(buf));
+               prefix2str(&nhr.prefix, buf, sizeof(buf));
                zlog_debug(
                        "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
-                       __PRETTY_FUNCTION__, buf, pim->vrf->name, nexthop_num,
-                       pnc->nexthop_num, vrf_id, pnc->upstream_hash->count,
+                       __PRETTY_FUNCTION__, buf, pim->vrf->name,
+                       nhr.nexthop_num, pnc->nexthop_num, vrf_id,
+                       pnc->upstream_hash->count,
                        listcount(pnc->rp_list));
        }
 
index 450faf75bbc1138894ab49ff618e92c7f1cc17be..791680a9114237c1b16a264393b4a892453d52ed 100644 (file)
@@ -232,11 +232,6 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
        return writes;
 }
 
-int pim_global_config_write(struct vty *vty)
-{
-       return pim_global_config_write_worker(pimg, vty);
-}
-
 int pim_interface_config_write(struct vty *vty)
 {
        struct pim_instance *pim;
index aef90cacc39a0061d57e74546a8aef59b2b3db62..22ac3333e4d9a8b41d25ca1ab1e1b8398b4574c2 100644 (file)
@@ -23,7 +23,6 @@
 #include "vty.h"
 
 int pim_debug_config_write(struct vty *vty);
-int pim_global_config_write(struct vty *vty);
 int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty);
 int pim_interface_config_write(struct vty *vty);
 
index 6e825587664553664dea2d3dab4463b8b28e9762..2970dcee5e292847fec582a85d6991d13ea4a01e 100644 (file)
@@ -765,8 +765,6 @@ void pim_zebra_init(void)
                zlog_info("zclient_init cleared redistribution request");
        }
 
-       zassert(zclient->redist_default == ZEBRA_ROUTE_PIM);
-
        /* Request all redistribution */
        for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
                if (i == zclient->redist_default)
index 56808bc8ad05feeb40273450dbc2736486f1483b..9e5cb7fe54f4801db5e9d96bbbbea1a44de084b7 100644 (file)
@@ -25,8 +25,9 @@
 #include "privs.h"
 #include "queue.h"
 #include "filter.h"
+#include "frr_pthread.h"
 
-#include "bgpd/bgpd.h"
+#include "bgpd/bgpd.c"
 #include "bgpd/bgp_aspath.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_packet.h"
@@ -1272,6 +1273,9 @@ static int handle_attr_test(struct aspath_tests *t)
        struct aspath *asp;
        size_t datalen;
 
+       bgp_pthreads_init();
+       frr_pthread_get(PTHREAD_KEEPALIVES)->running = true;
+
        asp = make_aspath(t->segment->asdata, t->segment->len, 0);
 
        peer.curr = stream_new(BGP_MAX_PACKET_SIZE);
index a5092708e2e03dca6ddb9b23d0102c3059383250..69f5afb992b077c75b3eca72717c77fdac0df42f 100644 (file)
@@ -27,8 +27,9 @@
 #include "memory.h"
 #include "queue.h"
 #include "filter.h"
+#include "frr_pthread.h"
 
-#include "bgpd/bgpd.h"
+#include "bgpd/bgpd.c"
 #include "bgpd/bgp_open.h"
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_packet.h"
@@ -62,8 +63,8 @@ static struct test_segment {
 
        /* AFI/SAFI validation */
        int validate_afi;
-       afi_t afi;
-       safi_t safi;
+       iana_afi_t afi;
+       iana_safi_t safi;
 #define VALID_AFI 1
 #define INVALID_AFI 0
        int afi_valid;
@@ -112,8 +113,8 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP,
-               SAFI_UNICAST,
+               IANA_AFI_IPV4,
+               IANA_SAFI_UNICAST,
                VALID_AFI,
        },
        {
@@ -124,8 +125,8 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP6,
-               SAFI_UNICAST,
+               IANA_AFI_IPV6,
+               IANA_SAFI_UNICAST,
                VALID_AFI,
        },
        /* 5 */
@@ -137,8 +138,8 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP,
-               SAFI_MULTICAST,
+               IANA_AFI_IPV4,
+               IANA_SAFI_MULTICAST,
                VALID_AFI,
        },
        /* 6 */
@@ -150,7 +151,7 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP6,
+               IANA_AFI_IPV6,
                IANA_SAFI_MPLS_VPN,
                VALID_AFI,
        },
@@ -163,7 +164,7 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP6,
+               IANA_AFI_IPV6,
                IANA_SAFI_MPLS_VPN,
                VALID_AFI,
        },
@@ -176,7 +177,7 @@ static struct test_segment mp_segments[] = {
                SHOULD_PARSE,
                0,
                1,
-               AFI_IP,
+               IANA_AFI_IPV4,
                IANA_SAFI_MPLS_VPN,
                VALID_AFI,
        },
@@ -210,8 +211,8 @@ static struct test_segment mp_segments[] = {
                SHOULD_ERR,
                0,
                1,
-               AFI_IP,
-               SAFI_UNICAST,
+               IANA_AFI_IPV4,
+               IANA_SAFI_UNICAST,
                VALID_AFI,
        },
        {NULL, NULL, {0}, 0, 0}};
@@ -843,8 +844,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type)
                safi_t safi;
 
                /* Convert AFI, SAFI to internal values, check. */
-               if (bgp_map_afi_safi_iana2int(afi_int2iana(t->afi), t->safi,
-                                             &afi, &safi)) {
+               if (bgp_map_afi_safi_iana2int(t->afi, t->safi, &afi, &safi)) {
                        if (t->afi_valid == VALID_AFI)
                                failed++;
                }
@@ -915,6 +915,9 @@ int main(void)
        vrf_init(NULL, NULL, NULL, NULL);
        bgp_option_set(BGP_OPT_NO_LISTEN);
 
+       bgp_pthreads_init();
+       frr_pthread_get(PTHREAD_KEEPALIVES)->running = true;
+
        if (fileno(stdout) >= 0)
                tty = isatty(fileno(stdout));
 
index 6df784b9848730498761f61d4f45a629d8df63bc..8acb280ed350ee8bb05fab25ed1733b1a9730103 100644 (file)
@@ -63,13 +63,6 @@ static struct test_segment {
 #define SHOULD_PARSE   0
 #define SHOULD_ERR     -1
        int parses; /* whether it should parse or not */
-
-       /* AFI/SAFI validation */
-       afi_t afi;
-       safi_t safi;
-#define VALID_AFI 1
-#define INVALID_AFI 0
-       int afi_valid;
 } mp_reach_segments[] = {
        {
                "IPv6",
@@ -104,9 +97,6 @@ static struct test_segment {
                },
                (4 + 16 + 1 + 5),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-2",
@@ -150,9 +140,6 @@ static struct test_segment {
                },
                (4 + 16 + 1 + 5 + 9),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-default",
@@ -197,9 +184,6 @@ static struct test_segment {
                },
                (4 + 16 + 1 + 5 + 9 + 1),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-lnh",
@@ -260,9 +244,6 @@ static struct test_segment {
                },
                (4 + 32 + 1 + 5 + 9 + 1),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-nhlen",
@@ -323,9 +304,6 @@ static struct test_segment {
                },
                (4 + 32 + 1 + 5 + 9 + 1),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-nhlen2",
@@ -386,9 +364,6 @@ static struct test_segment {
                },
                (4 + 32 + 1 + 5 + 9 + 1),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-nhlen3",
@@ -417,9 +392,6 @@ static struct test_segment {
                },
                (4 + 16),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-nhlen4",
@@ -480,9 +452,6 @@ static struct test_segment {
                },
                (4 + 32 + 1 + 5 + 9 + 1),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-nlri",
@@ -543,9 +512,6 @@ static struct test_segment {
                },
                (4 + 32 + 1 + 5 + 9 + 1),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4",
@@ -561,9 +527,6 @@ static struct test_segment {
                },
                (4 + 4 + 1 + 3 + 4 + 1),
                SHOULD_PARSE,
-               AFI_IP,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-nhlen",
@@ -579,9 +542,6 @@ static struct test_segment {
                },
                (4 + 4 + 1 + 3 + 4 + 1),
                SHOULD_ERR,
-               AFI_IP,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-nlrilen",
@@ -596,9 +556,6 @@ static struct test_segment {
                },
                (4 + 4 + 1 + 3 + 2 + 1),
                SHOULD_ERR,
-               AFI_IP,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4",
@@ -623,9 +580,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_PARSE,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-bogus-plen",
@@ -659,9 +613,6 @@ static struct test_segment {
                },
                (3 + 1 + 3 * 4 + 1 + 3 + 4 + 1),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-plen1-short",
@@ -686,9 +637,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-plen1-long",
@@ -713,9 +661,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-plenn-long",
@@ -741,9 +686,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-plenn-short",
@@ -768,9 +710,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-bogus-rd-type",
@@ -795,9 +734,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_PARSE,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {
                "IPv4-VPNv4-0-nlri",
@@ -823,9 +759,6 @@ static struct test_segment {
                },
                (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
                SHOULD_ERR,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
 
        /* From bug #385 */
@@ -875,9 +808,6 @@ static struct test_segment {
                },
                37,
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
 
        {NULL, NULL, {0}, 0, 0}};
@@ -894,9 +824,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 5),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-unreach2",
@@ -910,9 +837,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 5 + 9),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-unreach-default",
@@ -926,9 +850,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 5 + 9 + 1),
                SHOULD_PARSE,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv6-unreach-nlri",
@@ -942,9 +863,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 5 + 9 + 1),
                SHOULD_ERR,
-               AFI_IP6,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-unreach",
@@ -957,9 +875,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 3 + 4 + 1),
                SHOULD_PARSE,
-               AFI_IP,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-unreach-nlrilen",
@@ -971,9 +886,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + 3 + 2 + 1),
                SHOULD_ERR,
-               AFI_IP,
-               SAFI_UNICAST,
-               VALID_AFI,
        },
        {
                "IPv4-unreach-VPNv4",
@@ -993,9 +905,6 @@ static struct test_segment mp_unreach_segments[] = {
                },
                (3 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
                SHOULD_PARSE,
-               AFI_IP,
-               IANA_SAFI_MPLS_VPN,
-               VALID_AFI,
        },
        {NULL, NULL, {0}, 0, 0}};
 
diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl
new file mode 100755 (executable)
index 0000000..8477383
--- /dev/null
@@ -0,0 +1,6487 @@
+#!/usr/bin/env perl
+# (c) 2001, Dave Jones. (the file handling bit)
+# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
+# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
+# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+use strict;
+use warnings;
+use POSIX;
+use File::Basename;
+use Cwd 'abs_path';
+use Term::ANSIColor qw(:constants);
+
+my $P = $0;
+my $D = dirname(abs_path($P));
+
+my $V = '0.32';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+
+my $quiet = 0;
+my $tree = 1;
+my $chk_signoff = 1;
+my $chk_patch = 1;
+my $tst_only;
+my $emacs = 0;
+my $terse = 0;
+my $showfile = 0;
+my $file = 0;
+my $git = 0;
+my %git_commits = ();
+my $check = 0;
+my $check_orig = 0;
+my $summary = 1;
+my $mailback = 0;
+my $summary_file = 0;
+my $show_types = 0;
+my $list_types = 0;
+my $fix = 0;
+my $fix_inplace = 0;
+my $root;
+my %debug;
+my %camelcase = ();
+my %use_type = ();
+my @use = ();
+my %ignore_type = ();
+my @ignore = ();
+my $help = 0;
+my $configuration_file = ".checkpatch.conf";
+my $max_line_length = 80;
+my $ignore_perl_version = 0;
+my $minimum_perl_version = 5.10.0;
+my $min_conf_desc_length = 4;
+my $spelling_file = "$D/spelling.txt";
+my $codespell = 0;
+my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $conststructsfile = "$D/const_structs.checkpatch";
+my $typedefsfile = "";
+my $color = "auto";
+my $allow_c99_comments = 1;
+
+sub help {
+       my ($exitcode) = @_;
+
+       print << "EOM";
+Usage: $P [OPTION]... [FILE]...
+Version: $V
+
+Options:
+  -q, --quiet                quiet
+  --no-tree                  run without a kernel tree
+  --no-signoff               do not check for 'Signed-off-by' line
+  --patch                    treat FILE as patchfile (default)
+  --emacs                    emacs compile window format
+  --terse                    one line per report
+  --showfile                 emit diffed file position, not input file position
+  -g, --git                  treat FILE as a single commit or git revision range
+                             single git commit with:
+                               <rev>
+                               <rev>^
+                               <rev>~n
+                             multiple git commits with:
+                               <rev1>..<rev2>
+                               <rev1>...<rev2>
+                               <rev>-<count>
+                             git merges are ignored
+  -f, --file                 treat FILE as regular source file
+  --subjective, --strict     enable more subjective tests
+  --list-types               list the possible message types
+  --types TYPE(,TYPE2...)    show only these comma separated message types
+  --ignore TYPE(,TYPE2...)   ignore various comma separated message types
+  --show-types               show the specific message type in the output
+  --max-line-length=n        set the maximum line length, if exceeded, warn
+  --min-conf-desc-length=n   set the min description length, if shorter, warn
+  --root=PATH                PATH to the kernel tree root
+  --no-summary               suppress the per-file summary
+  --mailback                 only produce a report in case of warnings/errors
+  --summary-file             include the filename in summary
+  --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
+                             'values', 'possible', 'type', and 'attr' (default
+                             is all off)
+  --test-only=WORD           report only warnings/errors containing WORD
+                             literally
+  --fix                      EXPERIMENTAL - may create horrible results
+                             If correctable single-line errors exist, create
+                             "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
+                             with potential errors corrected to the preferred
+                             checkpatch style
+  --fix-inplace              EXPERIMENTAL - may create horrible results
+                             Is the same as --fix, but overwrites the input
+                             file.  It's your fault if there's no backup or git
+  --ignore-perl-version      override checking of perl version.  expect
+                             runtime errors.
+  --codespell                Use the codespell dictionary for spelling/typos
+                             (default:/usr/share/codespell/dictionary.txt)
+  --codespellfile            Use this codespell dictionary
+  --typedefsfile             Read additional types from this file
+  --color[=WHEN]             Use colors 'always', 'never', or only when output
+                             is a terminal ('auto'). Default is 'auto'.
+  -h, --help, --version      display this help and exit
+
+When FILE is - read standard input.
+EOM
+
+       exit($exitcode);
+}
+
+sub uniq {
+       my %seen;
+       return grep { !$seen{$_}++ } @_;
+}
+
+sub list_types {
+       my ($exitcode) = @_;
+
+       my $count = 0;
+
+       local $/ = undef;
+
+       open(my $script, '<', abs_path($P)) or
+           die "$P: Can't read '$P' $!\n";
+
+       my $text = <$script>;
+       close($script);
+
+       my @types = ();
+       # Also catch when type or level is passed through a variable
+       for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
+               push (@types, $_);
+       }
+       @types = sort(uniq(@types));
+       print("#\tMessage type\n\n");
+       foreach my $type (@types) {
+               print(++$count . "\t" . $type . "\n");
+       }
+
+       exit($exitcode);
+}
+
+my $conf = which_conf($configuration_file);
+if (-f $conf) {
+       my @conf_args;
+       open(my $conffile, '<', "$conf")
+           or warn "$P: Can't find a readable $configuration_file file $!\n";
+
+       while (<$conffile>) {
+               my $line = $_;
+
+               $line =~ s/\s*\n?$//g;
+               $line =~ s/^\s*//g;
+               $line =~ s/\s+/ /g;
+
+               next if ($line =~ m/^\s*#/);
+               next if ($line =~ m/^\s*$/);
+
+               my @words = split(" ", $line);
+               foreach my $word (@words) {
+                       last if ($word =~ m/^#/);
+                       push (@conf_args, $word);
+               }
+       }
+       close($conffile);
+       unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+# Perl's Getopt::Long allows options to take optional arguments after a space.
+# Prevent --color by itself from consuming other arguments
+foreach (@ARGV) {
+       if ($_ eq "--color" || $_ eq "-color") {
+               $_ = "--color=$color";
+       }
+}
+
+GetOptions(
+       'q|quiet+'      => \$quiet,
+       'tree!'         => \$tree,
+       'signoff!'      => \$chk_signoff,
+       'patch!'        => \$chk_patch,
+       'emacs!'        => \$emacs,
+       'terse!'        => \$terse,
+       'showfile!'     => \$showfile,
+       'f|file!'       => \$file,
+       'g|git!'        => \$git,
+       'subjective!'   => \$check,
+       'strict!'       => \$check,
+       'ignore=s'      => \@ignore,
+       'types=s'       => \@use,
+       'show-types!'   => \$show_types,
+       'list-types!'   => \$list_types,
+       'max-line-length=i' => \$max_line_length,
+       'min-conf-desc-length=i' => \$min_conf_desc_length,
+       'root=s'        => \$root,
+       'summary!'      => \$summary,
+       'mailback!'     => \$mailback,
+       'summary-file!' => \$summary_file,
+       'fix!'          => \$fix,
+       'fix-inplace!'  => \$fix_inplace,
+       'ignore-perl-version!' => \$ignore_perl_version,
+       'debug=s'       => \%debug,
+       'test-only=s'   => \$tst_only,
+       'codespell!'    => \$codespell,
+       'codespellfile=s'       => \$codespellfile,
+       'typedefsfile=s'        => \$typedefsfile,
+       'color=s'       => \$color,
+       'no-color'      => \$color,     #keep old behaviors of -nocolor
+       'nocolor'       => \$color,     #keep old behaviors of -nocolor
+       'h|help'        => \$help,
+       'version'       => \$help
+) or help(1);
+
+help(0) if ($help);
+
+list_types(0) if ($list_types);
+
+$fix = 1 if ($fix_inplace);
+$check_orig = $check;
+
+my $exit = 0;
+
+if ($^V && $^V lt $minimum_perl_version) {
+       printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
+       if (!$ignore_perl_version) {
+               exit(1);
+       }
+}
+
+#if no filenames are given, push '-' to read patch from stdin
+if ($#ARGV < 0) {
+       push(@ARGV, '-');
+}
+
+if ($color =~ /^[01]$/) {
+       $color = !$color;
+} elsif ($color =~ /^always$/i) {
+       $color = 1;
+} elsif ($color =~ /^never$/i) {
+       $color = 0;
+} elsif ($color =~ /^auto$/i) {
+       $color = (-t STDOUT);
+} else {
+       die "Invalid color mode: $color\n";
+}
+
+sub hash_save_array_words {
+       my ($hashRef, $arrayRef) = @_;
+
+       my @array = split(/,/, join(',', @$arrayRef));
+       foreach my $word (@array) {
+               $word =~ s/\s*\n?$//g;
+               $word =~ s/^\s*//g;
+               $word =~ s/\s+/ /g;
+               $word =~ tr/[a-z]/[A-Z]/;
+
+               next if ($word =~ m/^\s*#/);
+               next if ($word =~ m/^\s*$/);
+
+               $hashRef->{$word}++;
+       }
+}
+
+sub hash_show_words {
+       my ($hashRef, $prefix) = @_;
+
+       if (keys %$hashRef) {
+               print "\nNOTE: $prefix message types:";
+               foreach my $word (sort keys %$hashRef) {
+                       print " $word";
+               }
+               print "\n";
+       }
+}
+
+hash_save_array_words(\%ignore_type, \@ignore);
+hash_save_array_words(\%use_type, \@use);
+
+my $dbg_values = 0;
+my $dbg_possible = 0;
+my $dbg_type = 0;
+my $dbg_attr = 0;
+for my $key (keys %debug) {
+       ## no critic
+       eval "\${dbg_$key} = '$debug{$key}';";
+       die "$@" if ($@);
+}
+
+my $rpt_cleaners = 0;
+
+if ($terse) {
+       $emacs = 1;
+       $quiet++;
+}
+
+if ($tree) {
+       if (defined $root) {
+               if (!top_of_kernel_tree($root)) {
+                       die "$P: $root: --root does not point at a valid tree\n";
+               }
+       } else {
+               if (top_of_kernel_tree('.')) {
+                       $root = '.';
+               } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
+                                               top_of_kernel_tree($1)) {
+                       $root = $1;
+               }
+       }
+
+       if (!defined $root) {
+               print "Must be run from the top-level dir. of a kernel tree\n";
+               exit(2);
+       }
+}
+
+my $emitted_corrupt = 0;
+
+our $Ident     = qr{
+                       [A-Za-z_][A-Za-z\d_]*
+                       (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
+               }x;
+our $Storage   = qr{extern|static|asmlinkage};
+our $Sparse    = qr{
+                       __user|
+                       __kernel|
+                       __force|
+                       __iomem|
+                       __must_check|
+                       __init_refok|
+                       __kprobes|
+                       __ref|
+                       __rcu|
+                       __private
+               }x;
+our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
+our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
+our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)};
+our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)};
+our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit};
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
+our $Attribute = qr{
+                       const|
+                       __percpu|
+                       __nocast|
+                       __safe|
+                       __bitwise|
+                       __packed__|
+                       __packed2__|
+                       __naked|
+                       __maybe_unused|
+                       __always_unused|
+                       __noreturn|
+                       __used|
+                       __cold|
+                       __pure|
+                       __noclone|
+                       __deprecated|
+                       __read_mostly|
+                       __kprobes|
+                       $InitAttribute|
+                       ____cacheline_aligned|
+                       ____cacheline_aligned_in_smp|
+                       ____cacheline_internodealigned_in_smp|
+                       __weak
+                 }x;
+our $Modifier;
+our $Inline    = qr{inline|__always_inline|noinline|__inline|__inline__};
+our $Member    = qr{->$Ident|\.$Ident|\[[^]]*\]};
+our $Lval      = qr{$Ident(?:$Member)*};
+
+our $Int_type  = qr{(?i)llu|ull|ll|lu|ul|l|u};
+our $Binary    = qr{(?i)0b[01]+$Int_type?};
+our $Hex       = qr{(?i)0x[0-9a-f]+$Int_type?};
+our $Int       = qr{[0-9]+$Int_type?};
+our $Octal     = qr{0[0-7]+$Int_type?};
+our $String    = qr{"[X\t]*"};
+our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
+our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
+our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?};
+our $Float     = qr{$Float_hex|$Float_dec|$Float_int};
+our $Constant  = qr{$Float|$Binary|$Octal|$Hex|$Int};
+our $Assignment        = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
+our $Compare    = qr{<=|>=|==|!=|<|(?<!-)>};
+our $Arithmetic = qr{\+|-|\*|\/|%};
+our $Operators = qr{
+                       <=|>=|==|!=|
+                       =>|->|<<|>>|<|>|!|~|
+                       &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
+                 }x;
+
+our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
+
+our $BasicType;
+our $NonptrType;
+our $NonptrTypeMisordered;
+our $NonptrTypeWithAttr;
+our $Type;
+our $TypeMisordered;
+our $Declare;
+our $DeclareMisordered;
+
+our $NON_ASCII_UTF8    = qr{
+       [\xC2-\xDF][\x80-\xBF]               # non-overlong 2-byte
+       |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
+       | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
+       |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
+       |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
+       | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
+       |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
+}x;
+
+our $UTF8      = qr{
+       [\x09\x0A\x0D\x20-\x7E]              # ASCII
+       | $NON_ASCII_UTF8
+}x;
+
+our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
+our $typeOtherOSTypedefs = qr{(?x:
+       u_(?:char|short|int|long) |          # bsd
+       u(?:nchar|short|int|long)            # sysv
+)};
+our $typeKernelTypedefs = qr{(?x:
+       (?:__)?(?:u|s|be|le)(?:8|16|32|64)|
+       atomic_t
+)};
+our $typeTypedefs = qr{(?x:
+       $typeC99Typedefs\b|
+       $typeOtherOSTypedefs\b|
+       $typeKernelTypedefs\b
+)};
+
+our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};
+
+our $logFunctions = qr{(?x:
+       printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
+       (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
+       TP_printk|
+       WARN(?:_RATELIMIT|_ONCE|)|
+       panic|
+       MODULE_[A-Z_]+|
+       seq_vprintf|seq_printf|seq_puts
+)};
+
+our $signature_tags = qr{(?xi:
+       Signed-off-by:|
+       Acked-by:|
+       Tested-by:|
+       Reviewed-by:|
+       Reported-by:|
+       Suggested-by:|
+       To:|
+       Cc:
+)};
+
+our @typeListMisordered = (
+       qr{char\s+(?:un)?signed},
+       qr{int\s+(?:(?:un)?signed\s+)?short\s},
+       qr{int\s+short(?:\s+(?:un)?signed)},
+       qr{short\s+int(?:\s+(?:un)?signed)},
+       qr{(?:un)?signed\s+int\s+short},
+       qr{short\s+(?:un)?signed},
+       qr{long\s+int\s+(?:un)?signed},
+       qr{int\s+long\s+(?:un)?signed},
+       qr{long\s+(?:un)?signed\s+int},
+       qr{int\s+(?:un)?signed\s+long},
+       qr{int\s+(?:un)?signed},
+       qr{int\s+long\s+long\s+(?:un)?signed},
+       qr{long\s+long\s+int\s+(?:un)?signed},
+       qr{long\s+long\s+(?:un)?signed\s+int},
+       qr{long\s+long\s+(?:un)?signed},
+       qr{long\s+(?:un)?signed},
+);
+
+our @typeList = (
+       qr{void},
+       qr{(?:(?:un)?signed\s+)?char},
+       qr{(?:(?:un)?signed\s+)?short\s+int},
+       qr{(?:(?:un)?signed\s+)?short},
+       qr{(?:(?:un)?signed\s+)?int},
+       qr{(?:(?:un)?signed\s+)?long\s+int},
+       qr{(?:(?:un)?signed\s+)?long\s+long\s+int},
+       qr{(?:(?:un)?signed\s+)?long\s+long},
+       qr{(?:(?:un)?signed\s+)?long},
+       qr{(?:un)?signed},
+       qr{float},
+       qr{double},
+       qr{bool},
+       qr{struct\s+$Ident},
+       qr{union\s+$Ident},
+       qr{enum\s+$Ident},
+       qr{${Ident}_t},
+       qr{${Ident}_handler},
+       qr{${Ident}_handler_fn},
+       @typeListMisordered,
+);
+
+our $C90_int_types = qr{(?x:
+       long\s+long\s+int\s+(?:un)?signed|
+       long\s+long\s+(?:un)?signed\s+int|
+       long\s+long\s+(?:un)?signed|
+       (?:(?:un)?signed\s+)?long\s+long\s+int|
+       (?:(?:un)?signed\s+)?long\s+long|
+       int\s+long\s+long\s+(?:un)?signed|
+       int\s+(?:(?:un)?signed\s+)?long\s+long|
+
+       long\s+int\s+(?:un)?signed|
+       long\s+(?:un)?signed\s+int|
+       long\s+(?:un)?signed|
+       (?:(?:un)?signed\s+)?long\s+int|
+       (?:(?:un)?signed\s+)?long|
+       int\s+long\s+(?:un)?signed|
+       int\s+(?:(?:un)?signed\s+)?long|
+
+       int\s+(?:un)?signed|
+       (?:(?:un)?signed\s+)?int
+)};
+
+our @typeListFile = ();
+our @typeListWithAttr = (
+       @typeList,
+       qr{struct\s+$InitAttribute\s+$Ident},
+       qr{union\s+$InitAttribute\s+$Ident},
+);
+
+our @modifierList = (
+       qr{fastcall},
+);
+our @modifierListFile = ();
+
+our @mode_permission_funcs = (
+       ["module_param", 3],
+       ["module_param_(?:array|named|string)", 4],
+       ["module_param_array_named", 5],
+       ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
+       ["proc_create(?:_data|)", 2],
+       ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
+       ["IIO_DEV_ATTR_[A-Z_]+", 1],
+       ["SENSOR_(?:DEVICE_|)ATTR_2", 2],
+       ["SENSOR_TEMPLATE(?:_2|)", 3],
+       ["__ATTR", 2],
+);
+
+#Create a search pattern for all these functions to speed up a loop below
+our $mode_perms_search = "";
+foreach my $entry (@mode_permission_funcs) {
+       $mode_perms_search .= '|' if ($mode_perms_search ne "");
+       $mode_perms_search .= $entry->[0];
+}
+
+our $mode_perms_world_writable = qr{
+       S_IWUGO         |
+       S_IWOTH         |
+       S_IRWXUGO       |
+       S_IALLUGO       |
+       0[0-7][0-7][2367]
+}x;
+
+our %mode_permission_string_types = (
+       "S_IRWXU" => 0700,
+       "S_IRUSR" => 0400,
+       "S_IWUSR" => 0200,
+       "S_IXUSR" => 0100,
+       "S_IRWXG" => 0070,
+       "S_IRGRP" => 0040,
+       "S_IWGRP" => 0020,
+       "S_IXGRP" => 0010,
+       "S_IRWXO" => 0007,
+       "S_IROTH" => 0004,
+       "S_IWOTH" => 0002,
+       "S_IXOTH" => 0001,
+       "S_IRWXUGO" => 0777,
+       "S_IRUGO" => 0444,
+       "S_IWUGO" => 0222,
+       "S_IXUGO" => 0111,
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $mode_perms_string_search = "";
+foreach my $entry (keys %mode_permission_string_types) {
+       $mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
+       $mode_perms_string_search .= $entry;
+}
+
+our $allowed_asm_includes = qr{(?x:
+       irq|
+       memory|
+       time|
+       reboot
+)};
+# memory.h: ARM has a custom one
+
+# Load common spelling mistakes and build regular expression list.
+my $misspellings;
+my %spelling_fix;
+
+if (open(my $spelling, '<', $spelling_file)) {
+       while (<$spelling>) {
+               my $line = $_;
+
+               $line =~ s/\s*\n?$//g;
+               $line =~ s/^\s*//g;
+
+               next if ($line =~ m/^\s*#/);
+               next if ($line =~ m/^\s*$/);
+
+               my ($suspect, $fix) = split(/\|\|/, $line);
+
+               $spelling_fix{$suspect} = $fix;
+       }
+       close($spelling);
+} else {
+       warn "No typos will be found - file '$spelling_file': $!\n";
+}
+
+if ($codespell) {
+       if (open(my $spelling, '<', $codespellfile)) {
+               while (<$spelling>) {
+                       my $line = $_;
+
+                       $line =~ s/\s*\n?$//g;
+                       $line =~ s/^\s*//g;
+
+                       next if ($line =~ m/^\s*#/);
+                       next if ($line =~ m/^\s*$/);
+                       next if ($line =~ m/, disabled/i);
+
+                       $line =~ s/,.*$//;
+
+                       my ($suspect, $fix) = split(/->/, $line);
+
+                       $spelling_fix{$suspect} = $fix;
+               }
+               close($spelling);
+       } else {
+               warn "No codespell typos will be found - file '$codespellfile': $!\n";
+       }
+}
+
+$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
+
+sub read_words {
+       my ($wordsRef, $file) = @_;
+
+       if (open(my $words, '<', $file)) {
+               while (<$words>) {
+                       my $line = $_;
+
+                       $line =~ s/\s*\n?$//g;
+                       $line =~ s/^\s*//g;
+
+                       next if ($line =~ m/^\s*#/);
+                       next if ($line =~ m/^\s*$/);
+                       if ($line =~ /\s/) {
+                               print("$file: '$line' invalid - ignored\n");
+                               next;
+                       }
+
+                       $$wordsRef .= '|' if ($$wordsRef ne "");
+                       $$wordsRef .= $line;
+               }
+               close($file);
+               return 1;
+       }
+
+       return 0;
+}
+
+my $const_structs = "";
+read_words(\$const_structs, $conststructsfile)
+    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+
+my $typeOtherTypedefs = "";
+if (length($typedefsfile)) {
+       read_words(\$typeOtherTypedefs, $typedefsfile)
+           or warn "No additional types will be considered - file '$typedefsfile': $!\n";
+}
+$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne "");
+
+sub build_types {
+       my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
+       my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
+       my $Misordered = "(?x:  \n" . join("|\n  ", @typeListMisordered) . "\n)";
+       my $allWithAttr = "(?x:  \n" . join("|\n  ", @typeListWithAttr) . "\n)";
+       $Modifier       = qr{(?:$Attribute|$Sparse|$mods)};
+       $BasicType      = qr{
+                               (?:$typeTypedefs\b)|
+                               (?:${all}\b)
+               }x;
+       $NonptrType     = qr{
+                       (?:$Modifier\s+|const\s+)*
+                       (?:
+                               (?:typeof|__typeof__)\s*\([^\)]*\)|
+                               (?:$typeTypedefs\b)|
+                               (?:${all}\b)
+                       )
+                       (?:\s+$Modifier|\s+const)*
+                 }x;
+       $NonptrTypeMisordered   = qr{
+                       (?:$Modifier\s+|const\s+)*
+                       (?:
+                               (?:${Misordered}\b)
+                       )
+                       (?:\s+$Modifier|\s+const)*
+                 }x;
+       $NonptrTypeWithAttr     = qr{
+                       (?:$Modifier\s+|const\s+)*
+                       (?:
+                               (?:typeof|__typeof__)\s*\([^\)]*\)|
+                               (?:$typeTypedefs\b)|
+                               (?:${allWithAttr}\b)
+                       )
+                       (?:\s+$Modifier|\s+const)*
+                 }x;
+       $Type   = qr{
+                       $NonptrType
+                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+                       (?:\s+$Inline|\s+$Modifier)*
+                 }x;
+       $TypeMisordered = qr{
+                       $NonptrTypeMisordered
+                       (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
+                       (?:\s+$Inline|\s+$Modifier)*
+                 }x;
+       $Declare        = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
+       $DeclareMisordered      = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered};
+}
+build_types();
+
+our $Typecast  = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
+
+# Using $balanced_parens, $LvalOrFunc, or $FuncArg
+# requires at least perl version v5.10.0
+# Any use must be runtime checked with $^V
+
+our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
+our $LvalOrFunc        = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
+our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
+
+our $declaration_macros = qr{(?x:
+       (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
+       (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
+       (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(
+)};
+
+sub deparenthesize {
+       my ($string) = @_;
+       return "" if (!defined($string));
+
+       while ($string =~ /^\s*\(.*\)\s*$/) {
+               $string =~ s@^\s*\(\s*@@;
+               $string =~ s@\s*\)\s*$@@;
+       }
+
+       $string =~ s@\s+@ @g;
+
+       return $string;
+}
+
+sub seed_camelcase_file {
+       my ($file) = @_;
+
+       return if (!(-f $file));
+
+       local $/;
+
+       open(my $include_file, '<', "$file")
+           or warn "$P: Can't read '$file' $!\n";
+       my $text = <$include_file>;
+       close($include_file);
+
+       my @lines = split('\n', $text);
+
+       foreach my $line (@lines) {
+               next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/);
+               if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) {
+                       $camelcase{$1} = 1;
+               } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) {
+                       $camelcase{$1} = 1;
+               } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) {
+                       $camelcase{$1} = 1;
+               }
+       }
+}
+
+sub is_maintained_obsolete {
+       my ($filename) = @_;
+
+       return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
+
+       my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+
+       return $status =~ /obsolete/i;
+}
+
+my $camelcase_seeded = 0;
+sub seed_camelcase_includes {
+       return if ($camelcase_seeded);
+
+       my $files;
+       my $camelcase_cache = "";
+       my @include_files = ();
+
+       $camelcase_seeded = 1;
+
+       if (-e ".git") {
+               my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
+               chomp $git_last_include_commit;
+               $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
+       } else {
+               my $last_mod_date = 0;
+               $files = `find $root/include -name "*.h"`;
+               @include_files = split('\n', $files);
+               foreach my $file (@include_files) {
+                       my $date = POSIX::strftime("%Y%m%d%H%M",
+                                                  localtime((stat $file)[9]));
+                       $last_mod_date = $date if ($last_mod_date < $date);
+               }
+               $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
+       }
+
+       if ($camelcase_cache ne "" && -f $camelcase_cache) {
+               open(my $camelcase_file, '<', "$camelcase_cache")
+                   or warn "$P: Can't read '$camelcase_cache' $!\n";
+               while (<$camelcase_file>) {
+                       chomp;
+                       $camelcase{$_} = 1;
+               }
+               close($camelcase_file);
+
+               return;
+       }
+
+       if (-e ".git") {
+               $files = `git ls-files "include/*.h"`;
+               @include_files = split('\n', $files);
+       }
+
+       foreach my $file (@include_files) {
+               seed_camelcase_file($file);
+       }
+
+       if ($camelcase_cache ne "") {
+               unlink glob ".checkpatch-camelcase.*";
+               open(my $camelcase_file, '>', "$camelcase_cache")
+                   or warn "$P: Can't write '$camelcase_cache' $!\n";
+               foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
+                       print $camelcase_file ("$_\n");
+               }
+               close($camelcase_file);
+       }
+}
+
+sub git_commit_info {
+       my ($commit, $id, $desc) = @_;
+
+       return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));
+
+       my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`;
+       $output =~ s/^\s*//gm;
+       my @lines = split("\n", $output);
+
+       return ($id, $desc) if ($#lines < 0);
+
+       if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) {
+# Maybe one day convert this block of bash into something that returns
+# all matching commit ids, but it's very slow...
+#
+#              echo "checking commits $1..."
+#              git rev-list --remotes | grep -i "^$1" |
+#              while read line ; do
+#                  git log --format='%H %s' -1 $line |
+#                  echo "commit $(cut -c 1-12,41-)"
+#              done
+       } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) {
+               $id = undef;
+       } else {
+               $id = substr($lines[0], 0, 12);
+               $desc = substr($lines[0], 41);
+       }
+
+       return ($id, $desc);
+}
+
+$chk_signoff = 0 if ($file);
+
+my @rawlines = ();
+my @lines = ();
+my @fixed = ();
+my @fixed_inserted = ();
+my @fixed_deleted = ();
+my $fixlinenr = -1;
+
+# If input is git commits, extract all commits from the commit expressions.
+# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
+die "$P: No git repository found\n" if ($git && !-e ".git");
+
+if ($git) {
+       my @commits = ();
+       foreach my $commit_expr (@ARGV) {
+               my $git_range;
+               if ($commit_expr =~ m/^(.*)-(\d+)$/) {
+                       $git_range = "-$2 $1";
+               } elsif ($commit_expr =~ m/\.\./) {
+                       $git_range = "$commit_expr";
+               } else {
+                       $git_range = "-1 $commit_expr";
+               }
+               my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+               foreach my $line (split(/\n/, $lines)) {
+                       $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
+                       next if (!defined($1) || !defined($2));
+                       my $sha1 = $1;
+                       my $subject = $2;
+                       unshift(@commits, $sha1);
+                       $git_commits{$sha1} = $subject;
+               }
+       }
+       die "$P: no git commits after extraction!\n" if (@commits == 0);
+       @ARGV = @commits;
+}
+
+my $vname;
+for my $filename (@ARGV) {
+       my $FILE;
+       if ($git) {
+               open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
+                       die "$P: $filename: git format-patch failed - $!\n";
+       } elsif ($file) {
+               open($FILE, '-|', "diff -u /dev/null $filename") ||
+                       die "$P: $filename: diff failed - $!\n";
+       } elsif ($filename eq '-') {
+               open($FILE, '<&STDIN');
+       } else {
+               open($FILE, '<', "$filename") ||
+                       die "$P: $filename: open failed - $!\n";
+       }
+       if ($filename eq '-') {
+               $vname = 'Your patch';
+       } elsif ($git) {
+               $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
+       } else {
+               $vname = $filename;
+       }
+       while (<$FILE>) {
+               chomp;
+               push(@rawlines, $_);
+       }
+       close($FILE);
+
+       if ($#ARGV > 0 && $quiet == 0) {
+               print '-' x length($vname) . "\n";
+               print "$vname\n";
+               print '-' x length($vname) . "\n";
+       }
+
+       if (!process($filename)) {
+               $exit = 1;
+       }
+       @rawlines = ();
+       @lines = ();
+       @fixed = ();
+       @fixed_inserted = ();
+       @fixed_deleted = ();
+       $fixlinenr = -1;
+       @modifierListFile = ();
+       @typeListFile = ();
+       build_types();
+}
+
+if (!$quiet) {
+       hash_show_words(\%use_type, "Used");
+       hash_show_words(\%ignore_type, "Ignored");
+
+       if ($^V lt 5.10.0) {
+               print << "EOM"
+
+NOTE: perl $^V is not modern enough to detect all possible issues.
+      An upgrade to at least perl v5.10.0 is suggested.
+EOM
+       }
+       if ($exit) {
+               print << "EOM"
+
+NOTE: If any of the errors are false positives, please report
+      them to the maintainer, see CHECKPATCH in MAINTAINERS.
+EOM
+       }
+}
+
+exit($exit);
+
+sub top_of_kernel_tree {
+       my ($root) = @_;
+
+       my @tree_check = (
+               "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile",
+               "README", "Documentation", "arch", "include", "drivers",
+               "fs", "init", "ipc", "kernel", "lib", "scripts",
+       );
+
+       foreach my $check (@tree_check) {
+               if (! -e $root . '/' . $check) {
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+sub parse_email {
+       my ($formatted_email) = @_;
+
+       my $name = "";
+       my $address = "";
+       my $comment = "";
+
+       if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
+               $name = $1;
+               $address = $2;
+               $comment = $3 if defined $3;
+       } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
+               $address = $1;
+               $comment = $2 if defined $2;
+       } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
+               $address = $1;
+               $comment = $2 if defined $2;
+               $formatted_email =~ s/$address.*$//;
+               $name = $formatted_email;
+               $name = trim($name);
+               $name =~ s/^\"|\"$//g;
+               # If there's a name left after stripping spaces and
+               # leading quotes, and the address doesn't have both
+               # leading and trailing angle brackets, the address
+               # is invalid. ie:
+               #   "joe smith joe@smith.com" bad
+               #   "joe smith <joe@smith.com" bad
+               if ($name ne "" && $address !~ /^<[^>]+>$/) {
+                       $name = "";
+                       $address = "";
+                       $comment = "";
+               }
+       }
+
+       $name = trim($name);
+       $name =~ s/^\"|\"$//g;
+       $address = trim($address);
+       $address =~ s/^\<|\>$//g;
+
+       if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+               $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+               $name = "\"$name\"";
+       }
+
+       return ($name, $address, $comment);
+}
+
+sub format_email {
+       my ($name, $address) = @_;
+
+       my $formatted_email;
+
+       $name = trim($name);
+       $name =~ s/^\"|\"$//g;
+       $address = trim($address);
+
+       if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+               $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+               $name = "\"$name\"";
+       }
+
+       if ("$name" eq "") {
+               $formatted_email = "$address";
+       } else {
+               $formatted_email = "$name <$address>";
+       }
+
+       return $formatted_email;
+}
+
+sub which {
+       my ($bin) = @_;
+
+       foreach my $path (split(/:/, $ENV{PATH})) {
+               if (-e "$path/$bin") {
+                       return "$path/$bin";
+               }
+       }
+
+       return "";
+}
+
+sub which_conf {
+       my ($conf) = @_;
+
+       foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+               if (-e "$path/$conf") {
+                       return "$path/$conf";
+               }
+       }
+
+       return "";
+}
+
+sub expand_tabs {
+       my ($str) = @_;
+
+       my $res = '';
+       my $n = 0;
+       for my $c (split(//, $str)) {
+               if ($c eq "\t") {
+                       $res .= ' ';
+                       $n++;
+                       for (; ($n % 8) != 0; $n++) {
+                               $res .= ' ';
+                       }
+                       next;
+               }
+               $res .= $c;
+               $n++;
+       }
+
+       return $res;
+}
+sub copy_spacing {
+       (my $res = shift) =~ tr/\t/ /c;
+       return $res;
+}
+
+sub line_stats {
+       my ($line) = @_;
+
+       # Drop the diff line leader and expand tabs
+       $line =~ s/^.//;
+       $line = expand_tabs($line);
+
+       # Pick the indent from the front of the line.
+       my ($white) = ($line =~ /^(\s*)/);
+
+       return (length($line), length($white));
+}
+
+my $sanitise_quote = '';
+
+sub sanitise_line_reset {
+       my ($in_comment) = @_;
+
+       if ($in_comment) {
+               $sanitise_quote = '*/';
+       } else {
+               $sanitise_quote = '';
+       }
+}
+sub sanitise_line {
+       my ($line) = @_;
+
+       my $res = '';
+       my $l = '';
+
+       my $qlen = 0;
+       my $off = 0;
+       my $c;
+
+       # Always copy over the diff marker.
+       $res = substr($line, 0, 1);
+
+       for ($off = 1; $off < length($line); $off++) {
+               $c = substr($line, $off, 1);
+
+               # Comments we are wacking completly including the begin
+               # and end, all to $;.
+               if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
+                       $sanitise_quote = '*/';
+
+                       substr($res, $off, 2, "$;$;");
+                       $off++;
+                       next;
+               }
+               if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
+                       $sanitise_quote = '';
+                       substr($res, $off, 2, "$;$;");
+                       $off++;
+                       next;
+               }
+               if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
+                       $sanitise_quote = '//';
+
+                       substr($res, $off, 2, $sanitise_quote);
+                       $off++;
+                       next;
+               }
+
+               # A \ in a string means ignore the next character.
+               if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
+                   $c eq "\\") {
+                       substr($res, $off, 2, 'XX');
+                       $off++;
+                       next;
+               }
+               # Regular quotes.
+               if ($c eq "'" || $c eq '"') {
+                       if ($sanitise_quote eq '') {
+                               $sanitise_quote = $c;
+
+                               substr($res, $off, 1, $c);
+                               next;
+                       } elsif ($sanitise_quote eq $c) {
+                               $sanitise_quote = '';
+                       }
+               }
+
+               #print "c<$c> SQ<$sanitise_quote>\n";
+               if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
+                       substr($res, $off, 1, $;);
+               } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
+                       substr($res, $off, 1, $;);
+               } elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
+                       substr($res, $off, 1, 'X');
+               } else {
+                       substr($res, $off, 1, $c);
+               }
+       }
+
+       if ($sanitise_quote eq '//') {
+               $sanitise_quote = '';
+       }
+
+       # The pathname on a #include may be surrounded by '<' and '>'.
+       if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
+               my $clean = 'X' x length($1);
+               $res =~ s@\<.*\>@<$clean>@;
+
+       # The whole of a #error is a string.
+       } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
+               my $clean = 'X' x length($1);
+               $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
+       }
+
+       if ($allow_c99_comments && $res =~ m@(//.*$)@) {
+               my $match = $1;
+               $res =~ s/\Q$match\E/"$;" x length($match)/e;
+       }
+
+       return $res;
+}
+
+sub get_quoted_string {
+       my ($line, $rawline) = @_;
+
+       return "" if ($line !~ m/($String)/g);
+       return substr($rawline, $-[0], $+[0] - $-[0]);
+}
+
+sub ctx_statement_block {
+       my ($linenr, $remain, $off) = @_;
+       my $line = $linenr - 1;
+       my $blk = '';
+       my $soff = $off;
+       my $coff = $off - 1;
+       my $coff_set = 0;
+
+       my $loff = 0;
+
+       my $type = '';
+       my $level = 0;
+       my @stack = ();
+       my $p;
+       my $c;
+       my $len = 0;
+
+       my $remainder;
+       while (1) {
+               @stack = (['', 0]) if ($#stack == -1);
+
+               #warn "CSB: blk<$blk> remain<$remain>\n";
+               # If we are about to drop off the end, pull in more
+               # context.
+               if ($off >= $len) {
+                       for (; $remain > 0; $line++) {
+                               last if (!defined $lines[$line]);
+                               next if ($lines[$line] =~ /^-/);
+                               $remain--;
+                               $loff = $len;
+                               $blk .= $lines[$line] . "\n";
+                               $len = length($blk);
+                               $line++;
+                               last;
+                       }
+                       # Bail if there is no further context.
+                       #warn "CSB: blk<$blk> off<$off> len<$len>\n";
+                       if ($off >= $len) {
+                               last;
+                       }
+                       if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
+                               $level++;
+                               $type = '#';
+                       }
+               }
+               $p = $c;
+               $c = substr($blk, $off, 1);
+               $remainder = substr($blk, $off);
+
+               #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
+
+               # Handle nested #if/#else.
+               if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
+                       push(@stack, [ $type, $level ]);
+               } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
+                       ($type, $level) = @{$stack[$#stack - 1]};
+               } elsif ($remainder =~ /^#\s*endif\b/) {
+                       ($type, $level) = @{pop(@stack)};
+               }
+
+               # Statement ends at the ';' or a close '}' at the
+               # outermost level.
+               if ($level == 0 && $c eq ';') {
+                       last;
+               }
+
+               # An else is really a conditional as long as its not else if
+               if ($level == 0 && $coff_set == 0 &&
+                               (!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
+                               $remainder =~ /^(else)(?:\s|{)/ &&
+                               $remainder !~ /^else\s+if\b/) {
+                       $coff = $off + length($1) - 1;
+                       $coff_set = 1;
+                       #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
+                       #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
+               }
+
+               if (($type eq '' || $type eq '(') && $c eq '(') {
+                       $level++;
+                       $type = '(';
+               }
+               if ($type eq '(' && $c eq ')') {
+                       $level--;
+                       $type = ($level != 0)? '(' : '';
+
+                       if ($level == 0 && $coff < $soff) {
+                               $coff = $off;
+                               $coff_set = 1;
+                               #warn "CSB: mark coff<$coff>\n";
+                       }
+               }
+               if (($type eq '' || $type eq '{') && $c eq '{') {
+                       $level++;
+                       $type = '{';
+               }
+               if ($type eq '{' && $c eq '}') {
+                       $level--;
+                       $type = ($level != 0)? '{' : '';
+
+                       if ($level == 0) {
+                               if (substr($blk, $off + 1, 1) eq ';') {
+                                       $off++;
+                               }
+                               last;
+                       }
+               }
+               # Preprocessor commands end at the newline unless escaped.
+               if ($type eq '#' && $c eq "\n" && $p ne "\\") {
+                       $level--;
+                       $type = '';
+                       $off++;
+                       last;
+               }
+               $off++;
+       }
+       # We are truly at the end, so shuffle to the next line.
+       if ($off == $len) {
+               $loff = $len + 1;
+               $line++;
+               $remain--;
+       }
+
+       my $statement = substr($blk, $soff, $off - $soff + 1);
+       my $condition = substr($blk, $soff, $coff - $soff + 1);
+
+       #warn "STATEMENT<$statement>\n";
+       #warn "CONDITION<$condition>\n";
+
+       #print "coff<$coff> soff<$off> loff<$loff>\n";
+
+       return ($statement, $condition,
+                       $line, $remain + 1, $off - $loff + 1, $level);
+}
+
+sub statement_lines {
+       my ($stmt) = @_;
+
+       # Strip the diff line prefixes and rip blank lines at start and end.
+       $stmt =~ s/(^|\n)./$1/g;
+       $stmt =~ s/^\s*//;
+       $stmt =~ s/\s*$//;
+
+       my @stmt_lines = ($stmt =~ /\n/g);
+
+       return $#stmt_lines + 2;
+}
+
+sub statement_rawlines {
+       my ($stmt) = @_;
+
+       my @stmt_lines = ($stmt =~ /\n/g);
+
+       return $#stmt_lines + 2;
+}
+
+sub statement_block_size {
+       my ($stmt) = @_;
+
+       $stmt =~ s/(^|\n)./$1/g;
+       $stmt =~ s/^\s*{//;
+       $stmt =~ s/}\s*$//;
+       $stmt =~ s/^\s*//;
+       $stmt =~ s/\s*$//;
+
+       my @stmt_lines = ($stmt =~ /\n/g);
+       my @stmt_statements = ($stmt =~ /;/g);
+
+       my $stmt_lines = $#stmt_lines + 2;
+       my $stmt_statements = $#stmt_statements + 1;
+
+       if ($stmt_lines > $stmt_statements) {
+               return $stmt_lines;
+       } else {
+               return $stmt_statements;
+       }
+}
+
+sub ctx_statement_full {
+       my ($linenr, $remain, $off) = @_;
+       my ($statement, $condition, $level);
+
+       my (@chunks);
+
+       # Grab the first conditional/block pair.
+       ($statement, $condition, $linenr, $remain, $off, $level) =
+                               ctx_statement_block($linenr, $remain, $off);
+       #print "F: c<$condition> s<$statement> remain<$remain>\n";
+       push(@chunks, [ $condition, $statement ]);
+       if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
+               return ($level, $linenr, @chunks);
+       }
+
+       # Pull in the following conditional/block pairs and see if they
+       # could continue the statement.
+       for (;;) {
+               ($statement, $condition, $linenr, $remain, $off, $level) =
+                               ctx_statement_block($linenr, $remain, $off);
+               #print "C: c<$condition> s<$statement> remain<$remain>\n";
+               last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
+               #print "C: push\n";
+               push(@chunks, [ $condition, $statement ]);
+       }
+
+       return ($level, $linenr, @chunks);
+}
+
+sub ctx_block_get {
+       my ($linenr, $remain, $outer, $open, $close, $off) = @_;
+       my $line;
+       my $start = $linenr - 1;
+       my $blk = '';
+       my @o;
+       my @c;
+       my @res = ();
+
+       my $level = 0;
+       my @stack = ($level);
+       for ($line = $start; $remain > 0; $line++) {
+               next if ($rawlines[$line] =~ /^-/);
+               $remain--;
+
+               $blk .= $rawlines[$line];
+
+               # Handle nested #if/#else.
+               if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
+                       push(@stack, $level);
+               } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
+                       $level = $stack[$#stack - 1];
+               } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
+                       $level = pop(@stack);
+               }
+
+               foreach my $c (split(//, $lines[$line])) {
+                       ##print "C<$c>L<$level><$open$close>O<$off>\n";
+                       if ($off > 0) {
+                               $off--;
+                               next;
+                       }
+
+                       if ($c eq $close && $level > 0) {
+                               $level--;
+                               last if ($level == 0);
+                       } elsif ($c eq $open) {
+                               $level++;
+                       }
+               }
+
+               if (!$outer || $level <= 1) {
+                       push(@res, $rawlines[$line]);
+               }
+
+               last if ($level == 0);
+       }
+
+       return ($level, @res);
+}
+sub ctx_block_outer {
+       my ($linenr, $remain) = @_;
+
+       my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);
+       return @r;
+}
+sub ctx_block {
+       my ($linenr, $remain) = @_;
+
+       my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+       return @r;
+}
+sub ctx_statement {
+       my ($linenr, $remain, $off) = @_;
+
+       my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+       return @r;
+}
+sub ctx_block_level {
+       my ($linenr, $remain) = @_;
+
+       return ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+}
+sub ctx_statement_level {
+       my ($linenr, $remain, $off) = @_;
+
+       return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+}
+
+sub ctx_locate_comment {
+       my ($first_line, $end_line) = @_;
+
+       # Catch a comment on the end of the line itself.
+       my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+       return $current_comment if (defined $current_comment);
+
+       # Look through the context and try and figure out if there is a
+       # comment.
+       my $in_comment = 0;
+       $current_comment = '';
+       for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {
+               my $line = $rawlines[$linenr - 1];
+               #warn "           $line\n";
+               if ($linenr == $first_line and $line =~ m@^.\s*\*@) {
+                       $in_comment = 1;
+               }
+               if ($line =~ m@/\*@) {
+                       $in_comment = 1;
+               }
+               if (!$in_comment && $current_comment ne '') {
+                       $current_comment = '';
+               }
+               $current_comment .= $line . "\n" if ($in_comment);
+               if ($line =~ m@\*/@) {
+                       $in_comment = 0;
+               }
+       }
+
+       chomp($current_comment);
+       return($current_comment);
+}
+sub ctx_has_comment {
+       my ($first_line, $end_line) = @_;
+       my $cmt = ctx_locate_comment($first_line, $end_line);
+
+       ##print "LINE: $rawlines[$end_line - 1 ]\n";
+       ##print "CMMT: $cmt\n";
+
+       return ($cmt ne '');
+}
+
+sub raw_line {
+       my ($linenr, $cnt) = @_;
+
+       my $offset = $linenr - 1;
+       $cnt++;
+
+       my $line;
+       while ($cnt) {
+               $line = $rawlines[$offset++];
+               next if (defined($line) && $line =~ /^-/);
+               $cnt--;
+       }
+
+       return $line;
+}
+
+sub cat_vet {
+       my ($vet) = @_;
+       my ($res, $coded);
+
+       $res = '';
+       while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
+               $res .= $1;
+               if ($2 ne '') {
+                       $coded = sprintf("^%c", unpack('C', $2) + 64);
+                       $res .= $coded;
+               }
+       }
+       $res =~ s/$/\$/;
+
+       return $res;
+}
+
+my $av_preprocessor = 0;
+my $av_pending;
+my @av_paren_type;
+my $av_pend_colon;
+
+sub annotate_reset {
+       $av_preprocessor = 0;
+       $av_pending = '_';
+       @av_paren_type = ('E');
+       $av_pend_colon = 'O';
+}
+
+sub annotate_values {
+       my ($stream, $type) = @_;
+
+       my $res;
+       my $var = '_' x length($stream);
+       my $cur = $stream;
+
+       print "$stream\n" if ($dbg_values > 1);
+
+       while (length($cur)) {
+               @av_paren_type = ('E') if ($#av_paren_type < 0);
+               print " <" . join('', @av_paren_type) .
+                               "> <$type> <$av_pending>" if ($dbg_values > 1);
+               if ($cur =~ /^(\s+)/o) {
+                       print "WS($1)\n" if ($dbg_values > 1);
+                       if ($1 =~ /\n/ && $av_preprocessor) {
+                               $type = pop(@av_paren_type);
+                               $av_preprocessor = 0;
+                       }
+
+               } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
+                       print "CAST($1)\n" if ($dbg_values > 1);
+                       push(@av_paren_type, $type);
+                       $type = 'c';
+
+               } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
+                       print "DECLARE($1)\n" if ($dbg_values > 1);
+                       $type = 'T';
+
+               } elsif ($cur =~ /^($Modifier)\s*/) {
+                       print "MODIFIER($1)\n" if ($dbg_values > 1);
+                       $type = 'T';
+
+               } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
+                       print "DEFINE($1,$2)\n" if ($dbg_values > 1);
+                       $av_preprocessor = 1;
+                       push(@av_paren_type, $type);
+                       if ($2 ne '') {
+                               $av_pending = 'N';
+                       }
+                       $type = 'E';
+
+               } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
+                       print "UNDEF($1)\n" if ($dbg_values > 1);
+                       $av_preprocessor = 1;
+                       push(@av_paren_type, $type);
+
+               } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
+                       print "PRE_START($1)\n" if ($dbg_values > 1);
+                       $av_preprocessor = 1;
+
+                       push(@av_paren_type, $type);
+                       push(@av_paren_type, $type);
+                       $type = 'E';
+
+               } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
+                       print "PRE_RESTART($1)\n" if ($dbg_values > 1);
+                       $av_preprocessor = 1;
+
+                       push(@av_paren_type, $av_paren_type[$#av_paren_type]);
+
+                       $type = 'E';
+
+               } elsif ($cur =~ /^(\#\s*(?:endif))/o) {
+                       print "PRE_END($1)\n" if ($dbg_values > 1);
+
+                       $av_preprocessor = 1;
+
+                       # Assume all arms of the conditional end as this
+                       # one does, and continue as if the #endif was not here.
+                       pop(@av_paren_type);
+                       push(@av_paren_type, $type);
+                       $type = 'E';
+
+               } elsif ($cur =~ /^(\\\n)/o) {
+                       print "PRECONT($1)\n" if ($dbg_values > 1);
+
+               } elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
+                       print "ATTR($1)\n" if ($dbg_values > 1);
+                       $av_pending = $type;
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
+                       print "SIZEOF($1)\n" if ($dbg_values > 1);
+                       if (defined $2) {
+                               $av_pending = 'V';
+                       }
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(if|while|for)\b/o) {
+                       print "COND($1)\n" if ($dbg_values > 1);
+                       $av_pending = 'E';
+                       $type = 'N';
+
+               } elsif ($cur =~/^(case)/o) {
+                       print "CASE($1)\n" if ($dbg_values > 1);
+                       $av_pend_colon = 'C';
+                       $type = 'N';
+
+               } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
+                       print "KEYWORD($1)\n" if ($dbg_values > 1);
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(\()/o) {
+                       print "PAREN('$1')\n" if ($dbg_values > 1);
+                       push(@av_paren_type, $av_pending);
+                       $av_pending = '_';
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(\))/o) {
+                       my $new_type = pop(@av_paren_type);
+                       if ($new_type ne '_') {
+                               $type = $new_type;
+                               print "PAREN('$1') -> $type\n"
+                                                       if ($dbg_values > 1);
+                       } else {
+                               print "PAREN('$1')\n" if ($dbg_values > 1);
+                       }
+
+               } elsif ($cur =~ /^($Ident)\s*\(/o) {
+                       print "FUNC($1)\n" if ($dbg_values > 1);
+                       $type = 'V';
+                       $av_pending = 'V';
+
+               } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
+                       if (defined $2 && $type eq 'C' || $type eq 'T') {
+                               $av_pend_colon = 'B';
+                       } elsif ($type eq 'E') {
+                               $av_pend_colon = 'L';
+                       }
+                       print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
+                       $type = 'V';
+
+               } elsif ($cur =~ /^($Ident|$Constant)/o) {
+                       print "IDENT($1)\n" if ($dbg_values > 1);
+                       $type = 'V';
+
+               } elsif ($cur =~ /^($Assignment)/o) {
+                       print "ASSIGN($1)\n" if ($dbg_values > 1);
+                       $type = 'N';
+
+               } elsif ($cur =~/^(;|{|})/) {
+                       print "END($1)\n" if ($dbg_values > 1);
+                       $type = 'E';
+                       $av_pend_colon = 'O';
+
+               } elsif ($cur =~/^(,)/) {
+                       print "COMMA($1)\n" if ($dbg_values > 1);
+                       $type = 'C';
+
+               } elsif ($cur =~ /^(\?)/o) {
+                       print "QUESTION($1)\n" if ($dbg_values > 1);
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(:)/o) {
+                       print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
+
+                       substr($var, length($res), 1, $av_pend_colon);
+                       if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
+                               $type = 'E';
+                       } else {
+                               $type = 'N';
+                       }
+                       $av_pend_colon = 'O';
+
+               } elsif ($cur =~ /^(\[)/o) {
+                       print "CLOSE($1)\n" if ($dbg_values > 1);
+                       $type = 'N';
+
+               } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
+                       my $variant;
+
+                       print "OPV($1)\n" if ($dbg_values > 1);
+                       if ($type eq 'V') {
+                               $variant = 'B';
+                       } else {
+                               $variant = 'U';
+                       }
+
+                       substr($var, length($res), 1, $variant);
+                       $type = 'N';
+
+               } elsif ($cur =~ /^($Operators)/o) {
+                       print "OP($1)\n" if ($dbg_values > 1);
+                       if ($1 ne '++' && $1 ne '--') {
+                               $type = 'N';
+                       }
+
+               } elsif ($cur =~ /(^.)/o) {
+                       print "C($1)\n" if ($dbg_values > 1);
+               }
+               if (defined $1) {
+                       $cur = substr($cur, length($1));
+                       $res .= $type x length($1);
+               }
+       }
+
+       return ($res, $var);
+}
+
+sub possible {
+       my ($possible, $line) = @_;
+       my $notPermitted = qr{(?:
+               ^(?:
+                       $Modifier|
+                       $Storage|
+                       $Type|
+                       DEFINE_\S+
+               )$|
+               ^(?:
+                       goto|
+                       return|
+                       case|
+                       else|
+                       asm|__asm__|
+                       do|
+                       \#|
+                       \#\#|
+               )(?:\s|$)|
+               ^(?:typedef|struct|enum)\b
+           )}x;
+       warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
+       if ($possible !~ $notPermitted) {
+               # Check for modifiers.
+               $possible =~ s/\s*$Storage\s*//g;
+               $possible =~ s/\s*$Sparse\s*//g;
+               if ($possible =~ /^\s*$/) {
+
+               } elsif ($possible =~ /\s/) {
+                       $possible =~ s/\s*$Type\s*//g;
+                       for my $modifier (split(' ', $possible)) {
+                               if ($modifier !~ $notPermitted) {
+                                       warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
+                                       push(@modifierListFile, $modifier);
+                               }
+                       }
+
+               } else {
+                       warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
+                       push(@typeListFile, $possible);
+               }
+               build_types();
+       } else {
+               warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
+       }
+}
+
+my $prefix = '';
+
+sub show_type {
+       my ($type) = @_;
+
+       $type =~ tr/[a-z]/[A-Z]/;
+
+       return defined $use_type{$type} if (scalar keys %use_type > 0);
+
+       return !defined $ignore_type{$type};
+}
+
+sub report {
+       my ($level, $type, $msg) = @_;
+
+       if (!show_type($type) ||
+           (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
+               return 0;
+       }
+       my $output = '';
+       if ($color) {
+               if ($level eq 'ERROR') {
+                       $output .= RED;
+               } elsif ($level eq 'WARNING') {
+                       $output .= YELLOW;
+               } else {
+                       $output .= GREEN;
+               }
+       }
+       $output .= $prefix . $level . ':';
+       if ($show_types) {
+               $output .= BLUE if ($color);
+               $output .= "$type:";
+       }
+       $output .= RESET if ($color);
+       $output .= ' ' . $msg . "\n";
+
+       if ($showfile) {
+               my @lines = split("\n", $output, -1);
+               splice(@lines, 1, 1);
+               $output = join("\n", @lines);
+       }
+       $output = (split('\n', $output))[0] . "\n" if ($terse);
+
+       push(our @report, $output);
+
+       return 1;
+}
+
+sub report_dump {
+       our @report;
+}
+
+sub fixup_current_range {
+       my ($lineRef, $offset, $length) = @_;
+
+       if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) {
+               my $o = $1;
+               my $l = $2;
+               my $no = $o + $offset;
+               my $nl = $l + $length;
+               $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/;
+       }
+}
+
+sub fix_inserted_deleted_lines {
+       my ($linesRef, $insertedRef, $deletedRef) = @_;
+
+       my $range_last_linenr = 0;
+       my $delta_offset = 0;
+
+       my $old_linenr = 0;
+       my $new_linenr = 0;
+
+       my $next_insert = 0;
+       my $next_delete = 0;
+
+       my @lines = ();
+
+       my $inserted = @{$insertedRef}[$next_insert++];
+       my $deleted = @{$deletedRef}[$next_delete++];
+
+       foreach my $old_line (@{$linesRef}) {
+               my $save_line = 1;
+               my $line = $old_line;   #don't modify the array
+               if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) {      #new filename
+                       $delta_offset = 0;
+               } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) {    #new hunk
+                       $range_last_linenr = $new_linenr;
+                       fixup_current_range(\$line, $delta_offset, 0);
+               }
+
+               while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) {
+                       $deleted = @{$deletedRef}[$next_delete++];
+                       $save_line = 0;
+                       fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1);
+               }
+
+               while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) {
+                       push(@lines, ${$inserted}{'LINE'});
+                       $inserted = @{$insertedRef}[$next_insert++];
+                       $new_linenr++;
+                       fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1);
+               }
+
+               if ($save_line) {
+                       push(@lines, $line);
+                       $new_linenr++;
+               }
+
+               $old_linenr++;
+       }
+
+       return @lines;
+}
+
+sub fix_insert_line {
+       my ($linenr, $line) = @_;
+
+       my $inserted = {
+               LINENR => $linenr,
+               LINE => $line,
+       };
+       push(@fixed_inserted, $inserted);
+}
+
+sub fix_delete_line {
+       my ($linenr, $line) = @_;
+
+       my $deleted = {
+               LINENR => $linenr,
+               LINE => $line,
+       };
+
+       push(@fixed_deleted, $deleted);
+}
+
+sub ERROR {
+       my ($type, $msg) = @_;
+
+       if (report("ERROR", $type, $msg)) {
+               our $clean = 0;
+               our $cnt_error++;
+               return 1;
+       }
+       return 0;
+}
+sub WARN {
+       my ($type, $msg) = @_;
+
+       if (report("WARNING", $type, $msg)) {
+               our $clean = 0;
+               our $cnt_warn++;
+               return 1;
+       }
+       return 0;
+}
+sub CHK {
+       my ($type, $msg) = @_;
+
+       if ($check && report("CHECK", $type, $msg)) {
+               our $clean = 0;
+               our $cnt_chk++;
+               return 1;
+       }
+       return 0;
+}
+
+sub check_absolute_file {
+       my ($absolute, $herecurr) = @_;
+       my $file = $absolute;
+
+       ##print "absolute<$absolute>\n";
+
+       # See if any suffix of this path is a path within the tree.
+       while ($file =~ s@^[^/]*/@@) {
+               if (-f "$root/$file") {
+                       ##print "file<$file>\n";
+                       last;
+               }
+       }
+       if (! -f _)  {
+               return 0;
+       }
+
+       # It is, so see if the prefix is acceptable.
+       my $prefix = $absolute;
+       substr($prefix, -length($file)) = '';
+
+       ##print "prefix<$prefix>\n";
+       if ($prefix ne ".../") {
+               WARN("USE_RELATIVE_PATH",
+                    "use relative pathname instead of absolute in changelog text\n" . $herecurr);
+       }
+}
+
+sub trim {
+       my ($string) = @_;
+
+       $string =~ s/^\s+|\s+$//g;
+
+       return $string;
+}
+
+sub ltrim {
+       my ($string) = @_;
+
+       $string =~ s/^\s+//;
+
+       return $string;
+}
+
+sub rtrim {
+       my ($string) = @_;
+
+       $string =~ s/\s+$//;
+
+       return $string;
+}
+
+sub string_find_replace {
+       my ($string, $find, $replace) = @_;
+
+       $string =~ s/$find/$replace/g;
+
+       return $string;
+}
+
+sub tabify {
+       my ($leading) = @_;
+
+       my $source_indent = 8;
+       my $max_spaces_before_tab = $source_indent - 1;
+       my $spaces_to_tab = " " x $source_indent;
+
+       #convert leading spaces to tabs
+       1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g;
+       #Remove spaces before a tab
+       1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g;
+
+       return "$leading";
+}
+
+sub pos_last_openparen {
+       my ($line) = @_;
+
+       my $pos = 0;
+
+       my $opens = $line =~ tr/\(/\(/;
+       my $closes = $line =~ tr/\)/\)/;
+
+       my $last_openparen = 0;
+
+       if (($opens == 0) || ($closes >= $opens)) {
+               return -1;
+       }
+
+       my $len = length($line);
+
+       for ($pos = 0; $pos < $len; $pos++) {
+               my $string = substr($line, $pos);
+               if ($string =~ /^($FuncArg|$balanced_parens)/) {
+                       $pos += length($1) - 1;
+               } elsif (substr($line, $pos, 1) eq '(') {
+                       $last_openparen = $pos;
+               } elsif (index($string, '(') == -1) {
+                       last;
+               }
+       }
+
+       return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
+}
+
+sub remove_defuns {
+       my @breakfast = ();
+       my $milktoast;
+       for my $tasty (@rawlines) {
+               $milktoast = $tasty;
+               if (($tasty =~ /^\+DEFPY/ ||
+                    $tasty =~ /^\+DEFUN/ ||
+                    $tasty =~ /^\+ALIAS/) .. ($tasty =~ /^\+\{/)) {
+                       $milktoast = "\n";
+               }
+               push(@breakfast, $milktoast);
+       }
+       @rawlines = @breakfast;
+}
+
+sub process {
+       my $filename = shift;
+
+       my $linenr=0;
+       my $prevline="";
+       my $prevrawline="";
+       my $stashline="";
+       my $stashrawline="";
+
+       my $length;
+       my $indent;
+       my $previndent=0;
+       my $stashindent=0;
+
+       our $clean = 1;
+       my $signoff = 0;
+       my $is_patch = 0;
+       my $in_header_lines = $file ? 0 : 1;
+       my $in_commit_log = 0;          #Scanning lines before patch
+       my $has_commit_log = 0;         #Encountered lines before patch
+       my $commit_log_possible_stack_dump = 0;
+       my $commit_log_long_line = 0;
+       my $commit_log_has_diff = 0;
+       my $reported_maintainer_file = 0;
+       my $non_utf8_charset = 0;
+
+       my $last_blank_line = 0;
+       my $last_coalesced_string_linenr = -1;
+
+       our @report = ();
+       our $cnt_lines = 0;
+       our $cnt_error = 0;
+       our $cnt_warn = 0;
+       our $cnt_chk = 0;
+
+       # Trace the real file/line as we go.
+       my $realfile = '';
+       my $realline = 0;
+       my $realcnt = 0;
+       my $here = '';
+       my $context_function;           #undef'd unless there's a known function
+       my $in_comment = 0;
+       my $comment_edge = 0;
+       my $first_line = 0;
+       my $p1_prefix = '';
+
+       my $prev_values = 'E';
+
+       # suppression flags
+       my %suppress_ifbraces;
+       my %suppress_whiletrailers;
+       my %suppress_export;
+       my $suppress_statement = 0;
+
+       my %signatures = ();
+
+       # Pre-scan the patch sanitizing the lines.
+       # Pre-scan the patch looking for any __setup documentation.
+       #
+       my @setup_docs = ();
+       my $setup_docs = 0;
+
+       my $camelcase_file_seeded = 0;
+
+       sanitise_line_reset();
+       remove_defuns();
+
+       my $line;
+       foreach my $rawline (@rawlines) {
+               $linenr++;
+               $line = $rawline;
+
+               push(@fixed, $rawline) if ($fix);
+
+               if ($rawline=~/^\+\+\+\s+(\S+)/) {
+                       $setup_docs = 0;
+                       if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) {
+                               $setup_docs = 1;
+                       }
+                       #next;
+               }
+               if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+                       $realline=$1-1;
+                       if (defined $2) {
+                               $realcnt=$3+1;
+                       } else {
+                               $realcnt=1+1;
+                       }
+                       $in_comment = 0;
+
+                       # Guestimate if this is a continuing comment.  Run
+                       # the context looking for a comment "edge".  If this
+                       # edge is a close comment then we must be in a comment
+                       # at context start.
+                       my $edge;
+                       my $cnt = $realcnt;
+                       for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
+                               next if (defined $rawlines[$ln - 1] &&
+                                        $rawlines[$ln - 1] =~ /^-/);
+                               $cnt--;
+                               #print "RAW<$rawlines[$ln - 1]>\n";
+                               last if (!defined $rawlines[$ln - 1]);
+                               if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
+                                   $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
+                                       ($edge) = $1;
+                                       last;
+                               }
+                       }
+                       if (defined $edge && $edge eq '*/') {
+                               $in_comment = 1;
+                       }
+
+                       # Guestimate if this is a continuing comment.  If this
+                       # is the start of a diff block and this line starts
+                       # ' *' then it is very likely a comment.
+                       if (!defined $edge &&
+                           $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
+                       {
+                               $in_comment = 1;
+                       }
+
+                       ##print "COMMENT:$in_comment edge<$edge> $rawline\n";
+                       sanitise_line_reset($in_comment);
+
+               } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
+                       # Standardise the strings and chars within the input to
+                       # simplify matching -- only bother with positive lines.
+                       $line = sanitise_line($rawline);
+               }
+               push(@lines, $line);
+
+               if ($realcnt > 1) {
+                       $realcnt-- if ($line =~ /^(?:\+| |$)/);
+               } else {
+                       $realcnt = 0;
+               }
+
+               #print "==>$rawline\n";
+               #print "-->$line\n";
+
+               if ($setup_docs && $line =~ /^\+/) {
+                       push(@setup_docs, $line);
+               }
+       }
+
+       $prefix = '';
+
+       $realcnt = 0;
+       $linenr = 0;
+       $fixlinenr = -1;
+       foreach my $line (@lines) {
+               $linenr++;
+               $fixlinenr++;
+               my $sline = $line;      #copy of $line
+               $sline =~ s/$;/ /g;     #with comments as spaces
+
+               my $rawline = $rawlines[$linenr - 1];
+
+#extract the line range in the file after the patch is applied
+               if (!$in_commit_log &&
+                   $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
+                       my $context = $4;
+                       $is_patch = 1;
+                       $first_line = $linenr + 1;
+                       $realline=$1-1;
+                       if (defined $2) {
+                               $realcnt=$3+1;
+                       } else {
+                               $realcnt=1+1;
+                       }
+                       annotate_reset();
+                       $prev_values = 'E';
+
+                       %suppress_ifbraces = ();
+                       %suppress_whiletrailers = ();
+                       %suppress_export = ();
+                       $suppress_statement = 0;
+                       if ($context =~ /\b(\w+)\s*\(/) {
+                               $context_function = $1;
+                       } else {
+                               undef $context_function;
+                       }
+                       next;
+
+# track the line number as we move through the hunk, note that
+# new versions of GNU diff omit the leading space on completely
+# blank context lines so we need to count that too.
+               } elsif ($line =~ /^( |\+|$)/) {
+                       $realline++;
+                       $realcnt-- if ($realcnt != 0);
+
+                       # Measure the line length and indent.
+                       ($length, $indent) = line_stats($rawline);
+
+                       # Track the previous line.
+                       ($prevline, $stashline) = ($stashline, $line);
+                       ($previndent, $stashindent) = ($stashindent, $indent);
+                       ($prevrawline, $stashrawline) = ($stashrawline, $rawline);
+
+                       #warn "line<$line>\n";
+
+               } elsif ($realcnt == 1) {
+                       $realcnt--;
+               }
+
+               my $hunk_line = ($realcnt != 0);
+
+               $here = "#$linenr: " if (!$file);
+               $here = "#$realline: " if ($file);
+
+               my $found_file = 0;
+               # extract the filename as it passes
+               if ($line =~ /^diff --git.*?(\S+)$/) {
+                       $realfile = $1;
+                       $realfile =~ s@^([^/]*)/@@ if (!$file);
+                       $in_commit_log = 0;
+                       $found_file = 1;
+               } elsif ($line =~ /^\+\+\+\s+(\S+)/) {
+                       $realfile = $1;
+                       $realfile =~ s@^([^/]*)/@@ if (!$file);
+                       $in_commit_log = 0;
+
+                       $p1_prefix = $1;
+                       if (!$file && $tree && $p1_prefix ne '' &&
+                           -e "$root/$p1_prefix") {
+                               WARN("PATCH_PREFIX",
+                                    "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
+                       }
+
+                       if ($realfile =~ m@^include/asm/@) {
+                               ERROR("MODIFIED_INCLUDE_ASM",
+                                     "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+                       }
+                       $found_file = 1;
+               }
+
+#make up the handle for any error we report on this line
+               if ($showfile) {
+                       $prefix = "$realfile:$realline: "
+               } elsif ($emacs) {
+                       if ($file) {
+                               $prefix = "$filename:$realline: ";
+                       } else {
+                               $prefix = "$filename:$linenr: ";
+                       }
+               }
+
+               if ($found_file) {
+                       if (is_maintained_obsolete($realfile)) {
+                               WARN("OBSOLETE",
+                                    "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy.  No unnecessary modifications please.\n");
+                       }
+                       if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
+                               $check = 1;
+                       } else {
+                               $check = $check_orig;
+                       }
+                       next;
+               }
+
+               $here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
+
+               my $hereline = "$here\n$rawline\n";
+               my $herecurr = "$here\n$rawline\n";
+               my $hereprev = "$here\n$prevrawline\n$rawline\n";
+
+               $cnt_lines++ if ($realcnt != 0);
+
+# Check if the commit log has what seems like a diff which can confuse patch
+               if ($in_commit_log && !$commit_log_has_diff &&
+                   (($line =~ m@^\s+diff\b.*a/[\w/]+@ &&
+                     $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) ||
+                    $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
+                    $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
+                       ERROR("DIFF_IN_COMMIT_MSG",
+                             "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr);
+                       $commit_log_has_diff = 1;
+               }
+
+# Check for incorrect file permissions
+               if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
+                       my $permhere = $here . "FILE: $realfile\n";
+                       if ($realfile !~ m@scripts/@ &&
+                           $realfile !~ /\.(py|pl|awk|sh)$/) {
+                               ERROR("EXECUTE_PERMISSIONS",
+                                     "do not set execute permissions for source files\n" . $permhere);
+                       }
+               }
+
+# Check the patch for a signoff:
+               if ($line =~ /^\s*signed-off-by:/i) {
+                       $signoff++;
+                       $in_commit_log = 0;
+               }
+
+# Check if MAINTAINERS is being updated.  If so, there's probably no need to
+# emit the "does MAINTAINERS need updating?" message on file add/move/delete
+               if ($line =~ /^\s*MAINTAINERS\s*\|/) {
+                       $reported_maintainer_file = 1;
+               }
+
+# Check signature styles
+               if (!$in_header_lines &&
+                   $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
+                       my $space_before = $1;
+                       my $sign_off = $2;
+                       my $space_after = $3;
+                       my $email = $4;
+                       my $ucfirst_sign_off = ucfirst(lc($sign_off));
+
+                       if ($sign_off !~ /$signature_tags/) {
+                               WARN("BAD_SIGN_OFF",
+                                    "Non-standard signature: $sign_off\n" . $herecurr);
+                       }
+                       if (defined $space_before && $space_before ne "") {
+                               if (WARN("BAD_SIGN_OFF",
+                                        "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =
+                                           "$ucfirst_sign_off $email";
+                               }
+                       }
+                       if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) {
+                               if (WARN("BAD_SIGN_OFF",
+                                        "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =
+                                           "$ucfirst_sign_off $email";
+                               }
+
+                       }
+                       if (!defined $space_after || $space_after ne " ") {
+                               if (WARN("BAD_SIGN_OFF",
+                                        "Use a single space after $ucfirst_sign_off\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =
+                                           "$ucfirst_sign_off $email";
+                               }
+                       }
+
+                       my ($email_name, $email_address, $comment) = parse_email($email);
+                       my $suggested_email = format_email(($email_name, $email_address));
+                       if ($suggested_email eq "") {
+                               ERROR("BAD_SIGN_OFF",
+                                     "Unrecognized email address: '$email'\n" . $herecurr);
+                       } else {
+                               my $dequoted = $suggested_email;
+                               $dequoted =~ s/^"//;
+                               $dequoted =~ s/" </ </;
+                               # Don't force email to have quotes
+                               # Allow just an angle bracketed address
+                               if ("$dequoted$comment" ne $email &&
+                                   "<$email_address>$comment" ne $email &&
+                                   "$suggested_email$comment" ne $email) {
+                                       WARN("BAD_SIGN_OFF",
+                                            "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
+                               }
+                       }
+
+# Check for duplicate signatures
+                       my $sig_nospace = $line;
+                       $sig_nospace =~ s/\s//g;
+                       $sig_nospace = lc($sig_nospace);
+                       if (defined $signatures{$sig_nospace}) {
+                               WARN("BAD_SIGN_OFF",
+                                    "Duplicate signature\n" . $herecurr);
+                       } else {
+                               $signatures{$sig_nospace} = 1;
+                       }
+               }
+
+# Check email subject for common tools that don't need to be mentioned
+               if ($in_header_lines &&
+                   $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) {
+                       WARN("EMAIL_SUBJECT",
+                            "A patch subject line should describe the change not the tool that found it\n" . $herecurr);
+               }
+
+# Check for old stable address
+               if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) {
+                       ERROR("STABLE_ADDRESS",
+                             "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr);
+               }
+
+# Check for unwanted Gerrit info
+               if ($in_commit_log && $line =~ /^\s*change-id:/i) {
+                       ERROR("GERRIT_CHANGE_ID",
+                             "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
+               }
+
+# Check if the commit log is in a possible stack dump
+               if ($in_commit_log && !$commit_log_possible_stack_dump &&
+                   ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
+                    $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
+                                       # timestamp
+                    $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) {
+                                       # stack dump address
+                       $commit_log_possible_stack_dump = 1;
+               }
+
+# Check for line lengths > 75 in commit log, warn once
+               if ($in_commit_log && !$commit_log_long_line &&
+                   length($line) > 75 &&
+                   !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ ||
+                                       # file delta changes
+                     $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ ||
+                                       # filename then :
+                     $line =~ /^\s*(?:Fixes:|Link:)/i ||
+                                       # A Fixes: or Link: line
+                     $commit_log_possible_stack_dump)) {
+                       WARN("COMMIT_LOG_LONG_LINE",
+                            "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
+                       $commit_log_long_line = 1;
+               }
+
+# Reset possible stack dump if a blank line is found
+               if ($in_commit_log && $commit_log_possible_stack_dump &&
+                   $line =~ /^\s*$/) {
+                       $commit_log_possible_stack_dump = 0;
+               }
+
+# Check for git id commit length and improperly formed commit descriptions
+               if ($in_commit_log && !$commit_log_possible_stack_dump &&
+                   $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i &&
+                   $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
+                   ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
+                    ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
+                     $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
+                     $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
+                       my $init_char = "c";
+                       my $orig_commit = "";
+                       my $short = 1;
+                       my $long = 0;
+                       my $case = 1;
+                       my $space = 1;
+                       my $hasdesc = 0;
+                       my $hasparens = 0;
+                       my $id = '0123456789ab';
+                       my $orig_desc = "commit description";
+                       my $description = "";
+
+                       if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) {
+                               $init_char = $1;
+                               $orig_commit = lc($2);
+                       } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) {
+                               $orig_commit = lc($1);
+                       }
+
+                       $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i);
+                       $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i);
+                       $space = 0 if ($line =~ /\bcommit [0-9a-f]/i);
+                       $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
+                       if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) {
+                               $orig_desc = $1;
+                               $hasparens = 1;
+                       } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i &&
+                                defined $rawlines[$linenr] &&
+                                $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) {
+                               $orig_desc = $1;
+                               $hasparens = 1;
+                       } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i &&
+                                defined $rawlines[$linenr] &&
+                                $rawlines[$linenr] =~ /^\s*[^"]+"\)/) {
+                               $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i;
+                               $orig_desc = $1;
+                               $rawlines[$linenr] =~ /^\s*([^"]+)"\)/;
+                               $orig_desc .= " " . $1;
+                               $hasparens = 1;
+                       }
+
+                       ($id, $description) = git_commit_info($orig_commit,
+                                                             $id, $orig_desc);
+
+                       if (defined($id) &&
+                          ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) {
+                               ERROR("GIT_COMMIT_ID",
+                                     "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr);
+                       }
+               }
+
+# Check for added, moved or deleted files
+               if (!$reported_maintainer_file && !$in_commit_log &&
+                   ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ ||
+                    $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
+                    ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
+                     (defined($1) || defined($2))))) {
+                       $is_patch = 1;
+                       $reported_maintainer_file = 1;
+                       WARN("FILE_PATH_CHANGES",
+                            "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
+               }
+
+# Check for wrappage within a valid hunk of the file
+               if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
+                       ERROR("CORRUPTED_PATCH",
+                             "patch seems to be corrupt (line wrapped?)\n" .
+                               $herecurr) if (!$emitted_corrupt++);
+               }
+
+# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
+               if (($realfile =~ /^$/ || $line =~ /^\+/) &&
+                   $rawline !~ m/^$UTF8*$/) {
+                       my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
+
+                       my $blank = copy_spacing($rawline);
+                       my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
+                       my $hereptr = "$hereline$ptr\n";
+
+                       CHK("INVALID_UTF8",
+                           "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
+               }
+
+# Check if it's the start of a commit log
+# (not a header line and we haven't seen the patch filename)
+               if ($in_header_lines && $realfile =~ /^$/ &&
+                   !($rawline =~ /^\s+(?:\S|$)/ ||
+                     $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) {
+                       $in_header_lines = 0;
+                       $in_commit_log = 1;
+                       $has_commit_log = 1;
+               }
+
+# Check if there is UTF-8 in a commit log when a mail header has explicitly
+# declined it, i.e defined some charset where it is missing.
+               if ($in_header_lines &&
+                   $rawline =~ /^Content-Type:.+charset="(.+)".*$/ &&
+                   $1 !~ /utf-8/i) {
+                       $non_utf8_charset = 1;
+               }
+
+               if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ &&
+                   $rawline =~ /$NON_ASCII_UTF8/) {
+                       WARN("UTF8_BEFORE_PATCH",
+                           "8-bit UTF-8 used in possible commit log\n" . $herecurr);
+               }
+
+# Check for absolute kernel paths in commit message
+               if ($tree && $in_commit_log) {
+                       while ($line =~ m{(?:^|\s)(/\S*)}g) {
+                               my $file = $1;
+
+                               if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+                                   check_absolute_file($1, $herecurr)) {
+                                       #
+                               } else {
+                                       check_absolute_file($file, $herecurr);
+                               }
+                       }
+               }
+
+# Check for various typo / spelling mistakes
+               if (defined($misspellings) &&
+                   ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
+                       while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) {
+                               my $typo = $1;
+                               my $typo_fix = $spelling_fix{lc($typo)};
+                               $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
+                               $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
+                               my $msg_level = \&WARN;
+                               $msg_level = \&CHK if ($file);
+                               if (&{$msg_level}("TYPO_SPELLING",
+                                                 "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
+                               }
+                       }
+               }
+
+# ignore non-hunk lines and lines being removed
+               next if (!$hunk_line || $line =~ /^-/);
+
+#trailing whitespace
+               if ($line =~ /^\+.*\015/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       if (ERROR("DOS_LINE_ENDINGS",
+                                 "DOS line endings\n" . $herevet) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/[\s\015]+$//;
+                       }
+               } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       if (ERROR("TRAILING_WHITESPACE",
+                                 "trailing whitespace\n" . $herevet) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\s+$//;
+                       }
+
+                       $rpt_cleaners = 1;
+               }
+
+# Check for FSF mailing addresses.
+               if ($rawline =~ /\bwrite to the Free/i ||
+                   $rawline =~ /\b675\s+Mass\s+Ave/i ||
+                   $rawline =~ /\b59\s+Temple\s+Pl/i ||
+                   $rawline =~ /\b51\s+Franklin\s+St/i) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       my $msg_level = \&ERROR;
+                       $msg_level = \&CHK if ($file);
+                       &{$msg_level}("FSF_MAILING_ADDRESS",
+                                     "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
+               }
+
+# check for Kconfig help text having a real description
+# Only applies when adding the entry originally, after that we do not have
+# sufficient context to determine whether it is indeed long enough.
+               if ($realfile =~ /Kconfig/ &&
+                   $line =~ /^\+\s*config\s+/) {
+                       my $length = 0;
+                       my $cnt = $realcnt;
+                       my $ln = $linenr + 1;
+                       my $f;
+                       my $is_start = 0;
+                       my $is_end = 0;
+                       for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
+                               $f = $lines[$ln - 1];
+                               $cnt-- if ($lines[$ln - 1] !~ /^-/);
+                               $is_end = $lines[$ln - 1] =~ /^\+/;
+
+                               next if ($f =~ /^-/);
+                               last if (!$file && $f =~ /^\@\@/);
+
+                               if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) {
+                                       $is_start = 1;
+                               } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
+                                       $length = -1;
+                               }
+
+                               $f =~ s/^.//;
+                               $f =~ s/#.*//;
+                               $f =~ s/^\s+//;
+                               next if ($f =~ /^$/);
+                               if ($f =~ /^\s*config\s/) {
+                                       $is_end = 1;
+                                       last;
+                               }
+                               $length++;
+                       }
+                       if ($is_start && $is_end && $length < $min_conf_desc_length) {
+                               WARN("CONFIG_DESCRIPTION",
+                                    "please write a paragraph that describes the config symbol fully\n" . $herecurr);
+                       }
+                       #print "is_start<$is_start> is_end<$is_end> length<$length>\n";
+               }
+
+# check for MAINTAINERS entries that don't have the right form
+               if ($realfile =~ /^MAINTAINERS$/ &&
+                   $rawline =~ /^\+[A-Z]:/ &&
+                   $rawline !~ /^\+[A-Z]:\t\S/) {
+                       if (WARN("MAINTAINERS_STYLE",
+                                "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+                       }
+               }
+
+# discourage the use of boolean for type definition attributes of Kconfig options
+               if ($realfile =~ /Kconfig/ &&
+                   $line =~ /^\+\s*\bboolean\b/) {
+                       WARN("CONFIG_TYPE_BOOLEAN",
+                            "Use of boolean is deprecated, please use bool instead.\n" . $herecurr);
+               }
+
+               if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
+                   ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
+                       my $flag = $1;
+                       my $replacement = {
+                               'EXTRA_AFLAGS' =>   'asflags-y',
+                               'EXTRA_CFLAGS' =>   'ccflags-y',
+                               'EXTRA_CPPFLAGS' => 'cppflags-y',
+                               'EXTRA_LDFLAGS' =>  'ldflags-y',
+                       };
+
+                       WARN("DEPRECATED_VARIABLE",
+                            "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
+               }
+
+# check for DT compatible documentation
+               if (defined $root &&
+                       (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
+                        ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) {
+
+                       my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
+
+                       my $dt_path = $root . "/Documentation/devicetree/bindings/";
+                       my $vp_file = $dt_path . "vendor-prefixes.txt";
+
+                       foreach my $compat (@compats) {
+                               my $compat2 = $compat;
+                               $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/;
+                               my $compat3 = $compat;
+                               $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/;
+                               `grep -Erq "$compat|$compat2|$compat3" $dt_path`;
+                               if ( $? >> 8 ) {
+                                       WARN("UNDOCUMENTED_DT_STRING",
+                                            "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr);
+                               }
+
+                               next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
+                               my $vendor = $1;
+                               `grep -Eq "^$vendor\\b" $vp_file`;
+                               if ( $? >> 8 ) {
+                                       WARN("UNDOCUMENTED_DT_STRING",
+                                            "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
+                               }
+                       }
+               }
+
+# check we are in a valid source file if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/);
+
+# line length limit (with some exclusions)
+#
+# There are a few types of lines that may extend beyond $max_line_length:
+#      logging functions like pr_info that end in a string
+#      lines with a single string
+#      #defines that are a single string
+#
+# There are 3 different line length message types:
+# LONG_LINE_COMMENT    a comment starts before but extends beyond $max_line_length
+# LONG_LINE_STRING     a string starts before but extends beyond $max_line_length
+# LONG_LINE            all other lines longer than $max_line_length
+#
+# if LONG_LINE is ignored, the other 2 types are also ignored
+#
+
+               if ($line =~ /^\+/ && $length > $max_line_length) {
+                       my $msg_type = "LONG_LINE";
+
+                       # Check the allowed long line types first
+
+                       # logging functions that end in a string that starts
+                       # before $max_line_length
+                       if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ &&
+                           length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+                               $msg_type = "";
+
+                       # lines with only strings (w/ possible termination)
+                       # #defines with only strings
+                       } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
+                                $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
+                               $msg_type = "";
+
+                       # More special cases
+                       } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ ||
+                                $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) {
+                               $msg_type = "";
+
+                       # Otherwise set the alternate message types
+
+                       # a comment starts before $max_line_length
+                       } elsif ($line =~ /($;[\s$;]*)$/ &&
+                                length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+                               $msg_type = "LONG_LINE_COMMENT"
+
+                       # a quoted string starts before $max_line_length
+                       } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ &&
+                                length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+                               $msg_type = "LONG_LINE_STRING"
+                       }
+
+                       if ($msg_type ne "" &&
+                           (show_type("LONG_LINE") || show_type($msg_type))) {
+                               WARN($msg_type,
+                                    "line over $max_line_length characters\n" . $herecurr);
+                       }
+               }
+
+# check for adding lines without a newline.
+               if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
+                       WARN("MISSING_EOF_NEWLINE",
+                            "adding a line without newline at end of file\n" . $herecurr);
+               }
+
+# Blackfin: use hi/lo macros
+               if ($realfile =~ m@arch/blackfin/.*\.S$@) {
+                       if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) {
+                               my $herevet = "$here\n" . cat_vet($line) . "\n";
+                               ERROR("LO_MACRO",
+                                     "use the LO() macro, not (... & 0xFFFF)\n" . $herevet);
+                       }
+                       if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) {
+                               my $herevet = "$here\n" . cat_vet($line) . "\n";
+                               ERROR("HI_MACRO",
+                                     "use the HI() macro, not (... >> 16)\n" . $herevet);
+                       }
+               }
+
+# check we are in a valid source file C or perl if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/);
+
+# at the beginning of a line any tabs must come first and anything
+# more than 8 must use tabs.
+               if ($rawline =~ /^\+\s* \t\s*\S/ ||
+                   $rawline =~ /^\+\s*        \s*/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       $rpt_cleaners = 1;
+                       if (ERROR("CODE_INDENT",
+                                 "code indent should use tabs where possible\n" . $herevet) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+                       }
+               }
+
+# check for space before tabs.
+               if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       if (WARN("SPACE_BEFORE_TAB",
+                               "please, no space before tabs\n" . $herevet) &&
+                           $fix) {
+                               while ($fixed[$fixlinenr] =~
+                                          s/(^\+.*) {8,8}\t/$1\t\t/) {}
+                               while ($fixed[$fixlinenr] =~
+                                          s/(^\+.*) +\t/$1\t/) {}
+                       }
+               }
+
+# check for && or || at the start of a line
+               if ($rawline =~ /^\+\s*(&&|\|\|)/) {
+                       CHK("LOGICAL_CONTINUATIONS",
+                           "Logical continuations should be on the previous line\n" . $hereprev);
+               }
+
+# check indentation starts on a tab stop
+               if ($^V && $^V ge 5.10.0 &&
+                   $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) {
+                       my $indent = length($1);
+                       if ($indent % 8) {
+                               if (WARN("TABSTOP",
+                                        "Statements should start on a tabstop\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e;
+                               }
+                       }
+               }
+
+# check multi-line statement indentation matches previous line
+               if ($^V && $^V ge 5.10.0 &&
+                   $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
+                       $prevline =~ /^\+(\t*)(.*)$/;
+                       my $oldindent = $1;
+                       my $rest = $2;
+
+                       my $pos = pos_last_openparen($rest);
+                       if ($pos >= 0) {
+                               $line =~ /^(\+| )([ \t]*)/;
+                               my $newindent = $2;
+
+                               my $goodtabindent = $oldindent .
+                                       "\t" x ($pos / 8) .
+                                       " "  x ($pos % 8);
+                               my $goodspaceindent = $oldindent . " "  x $pos;
+
+                               if ($newindent ne $goodtabindent &&
+                                   $newindent ne $goodspaceindent) {
+
+                                       if (CHK("PARENTHESIS_ALIGNMENT",
+                                               "Alignment should match open parenthesis\n" . $hereprev) &&
+                                           $fix && $line =~ /^\+/) {
+                                               $fixed[$fixlinenr] =~
+                                                   s/^\+[ \t]*/\+$goodtabindent/;
+                                       }
+                               }
+                       }
+               }
+
+# check for space after cast like "(int) foo" or "(struct foo) bar"
+# avoid checking a few false positives:
+#   "sizeof(<type>)" or "__alignof__(<type>)"
+#   function pointer declarations like "(*foo)(int) = bar;"
+#   structure definitions like "(struct foo) { 0 };"
+#   multiline macros that define functions
+#   known attributes or the __attribute__ keyword
+               if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ &&
+                   (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) {
+                       if (CHK("SPACING",
+                               "No space is necessary after a cast\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/(\(\s*$Type\s*\))[ \t]+/$1/;
+                       }
+               }
+
+# Block comment styles
+# Networking with an initial /*
+               if ($realfile =~ m@^(drivers/net/|net/)@ &&
+                   $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
+                   $rawline =~ /^\+[ \t]*\*/ &&
+                   $realline > 2) {
+                       WARN("NETWORKING_BLOCK_COMMENT_STYLE",
+                            "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
+               }
+
+# Block comments use * on subsequent lines
+               if ($prevline =~ /$;[ \t]*$/ &&                 #ends in comment
+                   $prevrawline =~ /^\+.*?\/\*/ &&             #starting /*
+                   $prevrawline !~ /\*\/[ \t]*$/ &&            #no trailing */
+                   $rawline =~ /^\+/ &&                        #line is new
+                   $rawline !~ /^\+[ \t]*\*/) {                #no leading *
+                       WARN("BLOCK_COMMENT_STYLE",
+                            "Block comments use * on subsequent lines\n" . $hereprev);
+               }
+
+# Block comments use */ on trailing lines
+               if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ &&       #trailing */
+                   $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ &&      #inline /*...*/
+                   $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ &&       #trailing **/
+                   $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) {    #non blank */
+                       WARN("BLOCK_COMMENT_STYLE",
+                            "Block comments use a trailing */ on a separate line\n" . $herecurr);
+               }
+
+# Block comment * alignment
+               if ($prevline =~ /$;[ \t]*$/ &&                 #ends in comment
+                   $line =~ /^\+[ \t]*$;/ &&                   #leading comment
+                   $rawline =~ /^\+[ \t]*\*/ &&                #leading *
+                   (($prevrawline =~ /^\+.*?\/\*/ &&           #leading /*
+                     $prevrawline !~ /\*\/[ \t]*$/) ||         #no trailing */
+                    $prevrawline =~ /^\+[ \t]*\*/)) {          #leading *
+                       my $oldindent;
+                       $prevrawline =~ m@^\+([ \t]*/?)\*@;
+                       if (defined($1)) {
+                               $oldindent = expand_tabs($1);
+                       } else {
+                               $prevrawline =~ m@^\+(.*/?)\*@;
+                               $oldindent = expand_tabs($1);
+                       }
+                       $rawline =~ m@^\+([ \t]*)\*@;
+                       my $newindent = $1;
+                       $newindent = expand_tabs($newindent);
+                       if (length($oldindent) ne length($newindent)) {
+                               WARN("BLOCK_COMMENT_STYLE",
+                                    "Block comments should align the * on each line\n" . $hereprev);
+                       }
+               }
+
+# check for missing blank lines after struct/union declarations
+# with exceptions for various attributes and macros
+               if ($prevline =~ /^[\+ ]};?\s*$/ &&
+                   $line =~ /^\+/ &&
+                   !($line =~ /^\+\s*$/ ||
+                     $line =~ /^\+\s*EXPORT_SYMBOL/ ||
+                     $line =~ /^\+\s*MODULE_/i ||
+                     $line =~ /^\+\s*\#\s*(?:end|elif|else)/ ||
+                     $line =~ /^\+[a-z_]*init/ ||
+                     $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ ||
+                     $line =~ /^\+\s*DECLARE/ ||
+                     $line =~ /^\+\s*builtin_[\w_]*driver/ ||
+                     $line =~ /^\+\s*__setup/)) {
+                       if (CHK("LINE_SPACING",
+                               "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) &&
+                           $fix) {
+                               fix_insert_line($fixlinenr, "\+");
+                       }
+               }
+
+# check for multiple consecutive blank lines
+               if ($prevline =~ /^[\+ ]\s*$/ &&
+                   $line =~ /^\+\s*$/ &&
+                   $last_blank_line != ($linenr - 1)) {
+                       if (CHK("LINE_SPACING",
+                               "Please don't use multiple blank lines\n" . $hereprev) &&
+                           $fix) {
+                               fix_delete_line($fixlinenr, $rawline);
+                       }
+
+                       $last_blank_line = $linenr;
+               }
+
+# check for missing blank lines after declarations
+               if ($sline =~ /^\+\s+\S/ &&                     #Not at char 1
+                       # actual declarations
+                   ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+                       # function pointer declarations
+                    $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+                       # foo bar; where foo is some local typedef or #define
+                    $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+                       # known declaration macros
+                    $prevline =~ /^\+\s+$declaration_macros/) &&
+                       # for "else if" which can look like "$Ident $Ident"
+                   !($prevline =~ /^\+\s+$c90_Keywords\b/ ||
+                       # other possible extensions of declaration lines
+                     $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+                       # not starting a section or a macro "\" extended line
+                     $prevline =~ /(?:\{\s*|\\)$/) &&
+                       # looks like a declaration
+                   !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+                       # function pointer declarations
+                     $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+                       # foo bar; where foo is some local typedef or #define
+                     $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+                       # known declaration macros
+                     $sline =~ /^\+\s+$declaration_macros/ ||
+                       # start of struct or union or enum
+                     $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ ||
+                       # start or end of block or continuation of declaration
+                     $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
+                       # bitfield continuation
+                     $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
+                       # other possible extensions of declaration lines
+                     $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) &&
+                       # indentation of previous and current line are the same
+                   (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) {
+                       if (WARN("LINE_SPACING",
+                                "Missing a blank line after declarations\n" . $hereprev) &&
+                           $fix) {
+                               fix_insert_line($fixlinenr, "\+");
+                       }
+               }
+
+# check for spaces at the beginning of a line.
+# Exceptions:
+#  1) within comments
+#  2) indented preprocessor commands
+#  3) hanging labels
+               if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/)  {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       if (WARN("LEADING_SPACE",
+                                "please, no spaces at the start of a line\n" . $herevet) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+                       }
+               }
+
+# check we are in a valid C source file if not then ignore this hunk
+               next if ($realfile !~ /\.(h|c)$/);
+
+# check for unusual line ending [ or (
+               if ($line =~ /^\+.*([\[\(])\s*$/) {
+                       CHK("OPEN_ENDED_LINE",
+                           "Lines should not end with a '$1'\n" . $herecurr);
+               }
+
+# check if this appears to be the start function declaration, save the name
+               if ($sline =~ /^\+\{\s*$/ &&
+                   $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) {
+                       $context_function = $1;
+               }
+
+# check if this appears to be the end of function declaration
+               if ($sline =~ /^\+\}\s*$/) {
+                       undef $context_function;
+               }
+
+# check indentation of any line with a bare else
+# (but not if it is a multiple line "if (foo) return bar; else return baz;")
+# if the previous line is a break or return and is indented 1 tab more...
+               if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) {
+                       my $tabs = length($1) + 1;
+                       if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ ||
+                           ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ &&
+                            defined $lines[$linenr] &&
+                            $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) {
+                               WARN("UNNECESSARY_ELSE",
+                                    "else is not generally useful after a break or return\n" . $hereprev);
+                       }
+               }
+
+# check indentation of a line with a break;
+# if the previous line is a goto or return and is indented the same # of tabs
+               if ($sline =~ /^\+([\t]+)break\s*;\s*$/) {
+                       my $tabs = $1;
+                       if ($prevline =~ /^\+$tabs(?:goto|return)\b/) {
+                               WARN("UNNECESSARY_BREAK",
+                                    "break is not useful after a goto or return\n" . $hereprev);
+                       }
+               }
+
+# check for RCS/CVS revision markers
+               if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
+                       WARN("CVS_KEYWORD",
+                            "CVS style keyword markers, these will _not_ be updated\n". $herecurr);
+               }
+
+# Blackfin: don't use __builtin_bfin_[cs]sync
+               if ($line =~ /__builtin_bfin_csync/) {
+                       my $herevet = "$here\n" . cat_vet($line) . "\n";
+                       ERROR("CSYNC",
+                             "use the CSYNC() macro in asm/blackfin.h\n" . $herevet);
+               }
+               if ($line =~ /__builtin_bfin_ssync/) {
+                       my $herevet = "$here\n" . cat_vet($line) . "\n";
+                       ERROR("SSYNC",
+                             "use the SSYNC() macro in asm/blackfin.h\n" . $herevet);
+               }
+
+# check for old HOTPLUG __dev<foo> section markings
+               if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) {
+                       WARN("HOTPLUG_SECTION",
+                            "Using $1 is unnecessary\n" . $herecurr);
+               }
+
+# Check for potential 'bare' types
+               my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
+                   $realline_next);
+#print "LINE<$line>\n";
+               if ($linenr > $suppress_statement &&
+                   $realcnt && $sline =~ /.\s*\S/) {
+                       ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+                               ctx_statement_block($linenr, $realcnt, 0);
+                       $stat =~ s/\n./\n /g;
+                       $cond =~ s/\n./\n /g;
+
+#print "linenr<$linenr> <$stat>\n";
+                       # If this statement has no statement boundaries within
+                       # it there is no point in retrying a statement scan
+                       # until we hit end of it.
+                       my $frag = $stat; $frag =~ s/;+\s*$//;
+                       if ($frag !~ /(?:{|;)/) {
+#print "skip<$line_nr_next>\n";
+                               $suppress_statement = $line_nr_next;
+                       }
+
+                       # Find the real next line.
+                       $realline_next = $line_nr_next;
+                       if (defined $realline_next &&
+                           (!defined $lines[$realline_next - 1] ||
+                            substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
+                               $realline_next++;
+                       }
+
+                       my $s = $stat;
+                       $s =~ s/{.*$//s;
+
+                       # Ignore goto labels.
+                       if ($s =~ /$Ident:\*$/s) {
+
+                       # Ignore functions being called
+                       } elsif ($s =~ /^.\s*$Ident\s*\(/s) {
+
+                       } elsif ($s =~ /^.\s*else\b/s) {
+
+                       # declarations always start with types
+                       } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
+                               my $type = $1;
+                               $type =~ s/\s+/ /g;
+                               possible($type, "A:" . $s);
+
+                       # definitions in global scope can only start with types
+                       } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
+                               possible($1, "B:" . $s);
+                       }
+
+                       # any (foo ... *) is a pointer cast, and foo is a type
+                       while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
+                               possible($1, "C:" . $s);
+                       }
+
+                       # Check for any sort of function declaration.
+                       # int foo(something bar, other baz);
+                       # void (*store_gdt)(x86_descr_ptr *);
+                       if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) {
+                               my ($name_len) = length($1);
+
+                               my $ctx = $s;
+                               substr($ctx, 0, $name_len + 1, '');
+                               $ctx =~ s/\)[^\)]*$//;
+
+                               for my $arg (split(/\s*,\s*/, $ctx)) {
+                                       if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) {
+
+                                               possible($1, "D:" . $s);
+                                       }
+                               }
+                       }
+
+               }
+
+#
+# Checks which may be anchored in the context.
+#
+
+# Check for switch () and associated case and default
+# statements should be at the same indent.
+               if ($line=~/\bswitch\s*\(.*\)/) {
+                       my $err = '';
+                       my $sep = '';
+                       my @ctx = ctx_block_outer($linenr, $realcnt);
+                       shift(@ctx);
+                       for my $ctx (@ctx) {
+                               my ($clen, $cindent) = line_stats($ctx);
+                               if ($ctx =~ /^\+\s*(case\s+|default:)/ &&
+                                                       $indent != $cindent) {
+                                       $err .= "$sep$ctx\n";
+                                       $sep = '';
+                               } else {
+                                       $sep = "[...]\n";
+                               }
+                       }
+                       if ($err ne '') {
+                               ERROR("SWITCH_CASE_INDENT_LEVEL",
+                                     "switch and case should be at the same indent\n$hereline$err");
+                       }
+               }
+
+# if/while/etc brace do not go on next line, unless defining a do while loop,
+# or if that brace on the next line is for something else
+               if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
+                       my $pre_ctx = "$1$2";
+
+                       my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
+
+                       if ($line =~ /^\+\t{6,}/) {
+                               WARN("DEEP_INDENTATION",
+                                    "Too many leading tabs - consider code refactoring\n" . $herecurr);
+                       }
+
+                       my $ctx_cnt = $realcnt - $#ctx - 1;
+                       my $ctx = join("\n", @ctx);
+
+                       my $ctx_ln = $linenr;
+                       my $ctx_skip = $realcnt;
+
+                       while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
+                                       defined $lines[$ctx_ln - 1] &&
+                                       $lines[$ctx_ln - 1] =~ /^-/)) {
+                               ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
+                               $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
+                               $ctx_ln++;
+                       }
+
+                       #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+                       #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
+
+                       if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
+                               ERROR("OPEN_BRACE",
+                                     "that open brace { should be on the previous line\n" .
+                                       "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+                       }
+                       if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ &&
+                           $ctx =~ /\)\s*\;\s*$/ &&
+                           defined $lines[$ctx_ln - 1])
+                       {
+                               my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]);
+                               if ($nindent > $indent) {
+                                       WARN("TRAILING_SEMICOLON",
+                                            "trailing semicolon indicates no statements, indent implies otherwise\n" .
+                                               "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+                               }
+                       }
+               }
+
+# Check relative indent for conditionals and blocks.
+               if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+                       ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+                               ctx_statement_block($linenr, $realcnt, 0)
+                                       if (!defined $stat);
+                       my ($s, $c) = ($stat, $cond);
+
+                       substr($s, 0, length($c), '');
+
+                       # remove inline comments
+                       $s =~ s/$;/ /g;
+                       $c =~ s/$;/ /g;
+
+                       # Find out how long the conditional actually is.
+                       my @newlines = ($c =~ /\n/gs);
+                       my $cond_lines = 1 + $#newlines;
+
+                       # Make sure we remove the line prefixes as we have
+                       # none on the first line, and are going to readd them
+                       # where necessary.
+                       $s =~ s/\n./\n/gs;
+                       while ($s =~ /\n\s+\\\n/) {
+                               $cond_lines += $s =~ s/\n\s+\\\n/\n/g;
+                       }
+
+                       # We want to check the first line inside the block
+                       # starting at the end of the conditional, so remove:
+                       #  1) any blank line termination
+                       #  2) any opening brace { on end of the line
+                       #  3) any do (...) {
+                       my $continuation = 0;
+                       my $check = 0;
+                       $s =~ s/^.*\bdo\b//;
+                       $s =~ s/^\s*{//;
+                       if ($s =~ s/^\s*\\//) {
+                               $continuation = 1;
+                       }
+                       if ($s =~ s/^\s*?\n//) {
+                               $check = 1;
+                               $cond_lines++;
+                       }
+
+                       # Also ignore a loop construct at the end of a
+                       # preprocessor statement.
+                       if (($prevline =~ /^.\s*#\s*define\s/ ||
+                           $prevline =~ /\\\s*$/) && $continuation == 0) {
+                               $check = 0;
+                       }
+
+                       my $cond_ptr = -1;
+                       $continuation = 0;
+                       while ($cond_ptr != $cond_lines) {
+                               $cond_ptr = $cond_lines;
+
+                               # If we see an #else/#elif then the code
+                               # is not linear.
+                               if ($s =~ /^\s*\#\s*(?:else|elif)/) {
+                                       $check = 0;
+                               }
+
+                               # Ignore:
+                               #  1) blank lines, they should be at 0,
+                               #  2) preprocessor lines, and
+                               #  3) labels.
+                               if ($continuation ||
+                                   $s =~ /^\s*?\n/ ||
+                                   $s =~ /^\s*#\s*?/ ||
+                                   $s =~ /^\s*$Ident\s*:/) {
+                                       $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
+                                       if ($s =~ s/^.*?\n//) {
+                                               $cond_lines++;
+                                       }
+                               }
+                       }
+
+                       my (undef, $sindent) = line_stats("+" . $s);
+                       my $stat_real = raw_line($linenr, $cond_lines);
+
+                       # Check if either of these lines are modified, else
+                       # this is not this patch's fault.
+                       if (!defined($stat_real) ||
+                           $stat !~ /^\+/ && $stat_real !~ /^\+/) {
+                               $check = 0;
+                       }
+                       if (defined($stat_real) && $cond_lines > 1) {
+                               $stat_real = "[...]\n$stat_real";
+                       }
+
+                       #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
+
+                       if ($check && $s ne '' &&
+                           (($sindent % 8) != 0 ||
+                            ($sindent < $indent) ||
+                            ($sindent == $indent &&
+                             ($s !~ /^\s*(?:\}|\{|else\b)/)) ||
+                            ($sindent > $indent + 8))) {
+                               WARN("SUSPECT_CODE_INDENT",
+                                    "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
+                       }
+               }
+
+               # Track the 'values' across context and added lines.
+               my $opline = $line; $opline =~ s/^./ /;
+               my ($curr_values, $curr_vars) =
+                               annotate_values($opline . "\n", $prev_values);
+               $curr_values = $prev_values . $curr_values;
+               if ($dbg_values) {
+                       my $outline = $opline; $outline =~ s/\t/ /g;
+                       print "$linenr > .$outline\n";
+                       print "$linenr > $curr_values\n";
+                       print "$linenr >  $curr_vars\n";
+               }
+               $prev_values = substr($curr_values, -1);
+
+#ignore lines not being added
+               next if ($line =~ /^[^\+]/);
+
+# check for dereferences that span multiple lines
+               if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ &&
+                   $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) {
+                       $prevline =~ /($Lval\s*(?:\.|->))\s*$/;
+                       my $ref = $1;
+                       $line =~ /^.\s*($Lval)/;
+                       $ref .= $1;
+                       $ref =~ s/\s//g;
+                       WARN("MULTILINE_DEREFERENCE",
+                            "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev);
+               }
+
+# check for declarations of signed or unsigned without int
+               while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
+                       my $type = $1;
+                       my $var = $2;
+                       $var = "" if (!defined $var);
+                       if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) {
+                               my $sign = $1;
+                               my $pointer = $2;
+
+                               $pointer = "" if (!defined $pointer);
+
+                               if (WARN("UNSPECIFIED_INT",
+                                        "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) &&
+                                   $fix) {
+                                       my $decl = trim($sign) . " int ";
+                                       my $comp_pointer = $pointer;
+                                       $comp_pointer =~ s/\s//g;
+                                       $decl .= $comp_pointer;
+                                       $decl = rtrim($decl) if ($var eq "");
+                                       $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@;
+                               }
+                       }
+               }
+
+# TEST: allow direct testing of the type matcher.
+               if ($dbg_type) {
+                       if ($line =~ /^.\s*$Declare\s*$/) {
+                               ERROR("TEST_TYPE",
+                                     "TEST: is type\n" . $herecurr);
+                       } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
+                               ERROR("TEST_NOT_TYPE",
+                                     "TEST: is not type ($1 is)\n". $herecurr);
+                       }
+                       next;
+               }
+# TEST: allow direct testing of the attribute matcher.
+               if ($dbg_attr) {
+                       if ($line =~ /^.\s*$Modifier\s*$/) {
+                               ERROR("TEST_ATTR",
+                                     "TEST: is attr\n" . $herecurr);
+                       } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
+                               ERROR("TEST_NOT_ATTR",
+                                     "TEST: is not attr ($1 is)\n". $herecurr);
+                       }
+                       next;
+               }
+
+# check for initialisation to aggregates open brace on the next line
+               if ($line =~ /^.\s*{/ &&
+                   $prevline =~ /(?:^|[^=])=\s*$/) {
+                       if (ERROR("OPEN_BRACE",
+                                 "that open brace { should be on the previous line\n" . $hereprev) &&
+                           $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                               fix_delete_line($fixlinenr, $rawline);
+                               my $fixedline = $prevrawline;
+                               $fixedline =~ s/\s*=\s*$/ = {/;
+                               fix_insert_line($fixlinenr, $fixedline);
+                               $fixedline = $line;
+                               $fixedline =~ s/^(.\s*)\{\s*/$1/;
+                               fix_insert_line($fixlinenr, $fixedline);
+                       }
+               }
+
+#
+# Checks which are anchored on the added line.
+#
+
+# check for malformed paths in #include statements (uses RAW line)
+               if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) {
+                       my $path = $1;
+                       if ($path =~ m{//}) {
+                               ERROR("MALFORMED_INCLUDE",
+                                     "malformed #include filename\n" . $herecurr);
+                       }
+                       if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) {
+                               ERROR("UAPI_INCLUDE",
+                                     "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr);
+                       }
+               }
+
+# no C99 // comments
+               if ($line =~ m{//}) {
+                       if (ERROR("C99_COMMENTS",
+                                 "do not use C99 // comments\n" . $herecurr) &&
+                           $fix) {
+                               my $line = $fixed[$fixlinenr];
+                               if ($line =~ /\/\/(.*)$/) {
+                                       my $comment = trim($1);
+                                       $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@;
+                               }
+                       }
+               }
+               # Remove C99 comments.
+               $line =~ s@//.*@@;
+               $opline =~ s@//.*@@;
+
+# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider
+# the whole statement.
+#print "APW <$lines[$realline_next - 1]>\n";
+               if (defined $realline_next &&
+                   exists $lines[$realline_next - 1] &&
+                   !defined $suppress_export{$realline_next} &&
+                   ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+                    $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+                       # Handle definitions which produce identifiers with
+                       # a prefix:
+                       #   XXX(foo);
+                       #   EXPORT_SYMBOL(something_foo);
+                       my $name = $1;
+                       if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ &&
+                           $name =~ /^${Ident}_$2/) {
+#print "FOO C name<$name>\n";
+                               $suppress_export{$realline_next} = 1;
+
+                       } elsif ($stat !~ /(?:
+                               \n.}\s*$|
+                               ^.DEFINE_$Ident\(\Q$name\E\)|
+                               ^.DECLARE_$Ident\(\Q$name\E\)|
+                               ^.LIST_HEAD\(\Q$name\E\)|
+                               ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(|
+                               \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\()
+                           )/x) {
+#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n";
+                               $suppress_export{$realline_next} = 2;
+                       } else {
+                               $suppress_export{$realline_next} = 1;
+                       }
+               }
+               if (!defined $suppress_export{$linenr} &&
+                   $prevline =~ /^.\s*$/ &&
+                   ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+                    $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+#print "FOO B <$lines[$linenr - 1]>\n";
+                       $suppress_export{$linenr} = 2;
+               }
+               if (defined $suppress_export{$linenr} &&
+                   $suppress_export{$linenr} == 2) {
+                       WARN("EXPORT_SYMBOL",
+                            "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+               }
+
+# check for global initialisers.
+               if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) {
+                       if (ERROR("GLOBAL_INITIALISERS",
+                                 "do not initialise globals to $1\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/;
+                       }
+               }
+# check for static initialisers.
+               if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) {
+                       if (ERROR("INITIALISED_STATIC",
+                                 "do not initialise statics to $1\n" .
+                                     $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/;
+                       }
+               }
+
+# check for misordered declarations of char/short/int/long with signed/unsigned
+               while ($sline =~ m{(\b$TypeMisordered\b)}g) {
+                       my $tmp = trim($1);
+                       WARN("MISORDERED_TYPE",
+                            "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr);
+               }
+
+# check for static const char * arrays.
+               if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) {
+                       WARN("STATIC_CONST_CHAR_ARRAY",
+                            "static const char * array should probably be static const char * const\n" .
+                               $herecurr);
+               }
+
+# check for static char foo[] = "bar" declarations.
+               if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
+                       WARN("STATIC_CONST_CHAR_ARRAY",
+                            "static char array declaration should probably be static const char\n" .
+                               $herecurr);
+               }
+
+# check for const <foo> const where <foo> is not a pointer or array type
+               if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) {
+                       my $found = $1;
+                       if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) {
+                               WARN("CONST_CONST",
+                                    "'const $found const *' should probably be 'const $found * const'\n" . $herecurr);
+                       } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) {
+                               WARN("CONST_CONST",
+                                    "'const $found const' should probably be 'const $found'\n" . $herecurr);
+                       }
+               }
+
+# check for non-global char *foo[] = {"bar", ...} declarations.
+               if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
+                       WARN("STATIC_CONST_CHAR_ARRAY",
+                            "char * array declaration might be better as static const\n" .
+                               $herecurr);
+               }
+
+# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo)
+               if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) {
+                       my $array = $1;
+                       if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) {
+                               my $array_div = $1;
+                               if (WARN("ARRAY_SIZE",
+                                        "Prefer ARRAY_SIZE($array)\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/;
+                               }
+                       }
+               }
+
+# check for function declarations without arguments like "int foo()"
+               if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) {
+                       if (ERROR("FUNCTION_WITHOUT_ARGS",
+                                 "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/;
+                       }
+               }
+
+# check for new typedefs, only function parameters and sparse annotations
+# make sense.
+               if ($line =~ /\btypedef\s/ &&
+                   $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
+                   $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
+                   $line !~ /\b$typeTypedefs\b/ &&
+                   $line !~ /\b__bitwise\b/) {
+                       WARN("NEW_TYPEDEFS",
+                            "do not add new typedefs\n" . $herecurr);
+               }
+
+# * goes on variable not on type
+               # (char*[ const])
+               while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) {
+                       #print "AA<$1>\n";
+                       my ($ident, $from, $to) = ($1, $2, $2);
+
+                       # Should start with a space.
+                       $to =~ s/^(\S)/ $1/;
+                       # Should not end with a space.
+                       $to =~ s/\s+$//;
+                       # '*'s should not have spaces between.
+                       while ($to =~ s/\*\s+\*/\*\*/) {
+                       }
+
+##                     print "1: from<$from> to<$to> ident<$ident>\n";
+                       if ($from ne $to) {
+                               if (ERROR("POINTER_LOCATION",
+                                         "\"(foo$from)\" should be \"(foo$to)\"\n" .  $herecurr) &&
+                                   $fix) {
+                                       my $sub_from = $ident;
+                                       my $sub_to = $ident;
+                                       $sub_to =~ s/\Q$from\E/$to/;
+                                       $fixed[$fixlinenr] =~
+                                           s@\Q$sub_from\E@$sub_to@;
+                               }
+                       }
+               }
+               while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) {
+                       #print "BB<$1>\n";
+                       my ($match, $from, $to, $ident) = ($1, $2, $2, $3);
+
+                       # Should start with a space.
+                       $to =~ s/^(\S)/ $1/;
+                       # Should not end with a space.
+                       $to =~ s/\s+$//;
+                       # '*'s should not have spaces between.
+                       while ($to =~ s/\*\s+\*/\*\*/) {
+                       }
+                       # Modifiers should have spaces.
+                       $to =~ s/(\b$Modifier$)/$1 /;
+
+##                     print "2: from<$from> to<$to> ident<$ident>\n";
+                       if ($from ne $to && $ident !~ /^$Modifier$/) {
+                               if (ERROR("POINTER_LOCATION",
+                                         "\"foo${from}bar\" should be \"foo${to}bar\"\n" .  $herecurr) &&
+                                   $fix) {
+
+                                       my $sub_from = $match;
+                                       my $sub_to = $match;
+                                       $sub_to =~ s/\Q$from\E/$to/;
+                                       $fixed[$fixlinenr] =~
+                                           s@\Q$sub_from\E@$sub_to@;
+                               }
+                       }
+               }
+
+# avoid BUG() or BUG_ON()
+               if ($line =~ /\b(?:BUG|BUG_ON)\b/) {
+                       my $msg_level = \&WARN;
+                       $msg_level = \&CHK if ($file);
+                       &{$msg_level}("AVOID_BUG",
+                                     "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr);
+               }
+
+# avoid LINUX_VERSION_CODE
+               if ($line =~ /\bLINUX_VERSION_CODE\b/) {
+                       WARN("LINUX_VERSION_CODE",
+                            "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr);
+               }
+
+# check for uses of printk_ratelimit
+               if ($line =~ /\bprintk_ratelimit\s*\(/) {
+                       WARN("PRINTK_RATELIMITED",
+                            "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr);
+               }
+
+# printk should use KERN_* levels
+               if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) {
+                       WARN("PRINTK_WITHOUT_KERN_LEVEL",
+                            "printk() should include KERN_<LEVEL> facility level\n" . $herecurr);
+               }
+
+               if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) {
+                       my $orig = $1;
+                       my $level = lc($orig);
+                       $level = "warn" if ($level eq "warning");
+                       my $level2 = $level;
+                       $level2 = "dbg" if ($level eq "debug");
+                       WARN("PREFER_PR_LEVEL",
+                            "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(...  to printk(KERN_$orig ...\n" . $herecurr);
+               }
+
+               if ($line =~ /\bpr_warning\s*\(/) {
+                       if (WARN("PREFER_PR_LEVEL",
+                                "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\bpr_warning\b/pr_warn/;
+                       }
+               }
+
+               if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) {
+                       my $orig = $1;
+                       my $level = lc($orig);
+                       $level = "warn" if ($level eq "warning");
+                       $level = "dbg" if ($level eq "debug");
+                       WARN("PREFER_DEV_LEVEL",
+                            "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr);
+               }
+
+# ENOSYS means "bad syscall nr" and nothing else.  This will have a small
+# number of false positives, but assembly files are not checked, so at
+# least the arch entry code will not trigger this warning.
+               if ($line =~ /\bENOSYS\b/) {
+                       WARN("ENOSYS",
+                            "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr);
+               }
+
+# function brace can't be on same line, except for #defines of do while,
+# or if closed on same line
+               if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and
+                   !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) {
+                       if (ERROR("OPEN_BRACE",
+                                 "open brace '{' following function declarations go on the next line\n" . $herecurr) &&
+                           $fix) {
+                               fix_delete_line($fixlinenr, $rawline);
+                               my $fixed_line = $rawline;
+                               $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/;
+                               my $line1 = $1;
+                               my $line2 = $2;
+                               fix_insert_line($fixlinenr, ltrim($line1));
+                               fix_insert_line($fixlinenr, "\+{");
+                               if ($line2 !~ /^\s*$/) {
+                                       fix_insert_line($fixlinenr, "\+\t" . trim($line2));
+                               }
+                       }
+               }
+
+# open braces for enum, union and struct go on the same line.
+               if ($line =~ /^.\s*{/ &&
+                   $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) {
+                       if (ERROR("OPEN_BRACE",
+                                 "open brace '{' following $1 go on the same line\n" . $hereprev) &&
+                           $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                               fix_delete_line($fixlinenr, $rawline);
+                               my $fixedline = rtrim($prevrawline) . " {";
+                               fix_insert_line($fixlinenr, $fixedline);
+                               $fixedline = $rawline;
+                               $fixedline =~ s/^(.\s*)\{\s*/$1\t/;
+                               if ($fixedline !~ /^\+\s*$/) {
+                                       fix_insert_line($fixlinenr, $fixedline);
+                               }
+                       }
+               }
+
+# missing space after union, struct or enum definition
+               if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) {
+                       if (WARN("SPACING",
+                                "missing space after $1 definition\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/;
+                       }
+               }
+
+# Function pointer declarations
+# check spacing between type, funcptr, and args
+# canonical declaration is "type (*funcptr)(args...)"
+               if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) {
+                       my $declare = $1;
+                       my $pre_pointer_space = $2;
+                       my $post_pointer_space = $3;
+                       my $funcname = $4;
+                       my $post_funcname_space = $5;
+                       my $pre_args_space = $6;
+
+# the $Declare variable will capture all spaces after the type
+# so check it for a missing trailing missing space but pointer return types
+# don't need a space so don't warn for those.
+                       my $post_declare_space = "";
+                       if ($declare =~ /(\s+)$/) {
+                               $post_declare_space = $1;
+                               $declare = rtrim($declare);
+                       }
+                       if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) {
+                               WARN("SPACING",
+                                    "missing space after return type\n" . $herecurr);
+                               $post_declare_space = " ";
+                       }
+
+# unnecessary space "type  (*funcptr)(args...)"
+# This test is not currently implemented because these declarations are
+# equivalent to
+#      int  foo(int bar, ...)
+# and this is form shouldn't/doesn't generate a checkpatch warning.
+#
+#                      elsif ($declare =~ /\s{2,}$/) {
+#                              WARN("SPACING",
+#                                   "Multiple spaces after return type\n" . $herecurr);
+#                      }
+
+# unnecessary space "type ( *funcptr)(args...)"
+                       if (defined $pre_pointer_space &&
+                           $pre_pointer_space =~ /^\s/) {
+                               WARN("SPACING",
+                                    "Unnecessary space after function pointer open parenthesis\n" . $herecurr);
+                       }
+
+# unnecessary space "type (* funcptr)(args...)"
+                       if (defined $post_pointer_space &&
+                           $post_pointer_space =~ /^\s/) {
+                               WARN("SPACING",
+                                    "Unnecessary space before function pointer name\n" . $herecurr);
+                       }
+
+# unnecessary space "type (*funcptr )(args...)"
+                       if (defined $post_funcname_space &&
+                           $post_funcname_space =~ /^\s/) {
+                               WARN("SPACING",
+                                    "Unnecessary space after function pointer name\n" . $herecurr);
+                       }
+
+# unnecessary space "type (*funcptr) (args...)"
+                       if (defined $pre_args_space &&
+                           $pre_args_space =~ /^\s/) {
+                               WARN("SPACING",
+                                    "Unnecessary space before function pointer arguments\n" . $herecurr);
+                       }
+
+                       if (show_type("SPACING") && $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex;
+                       }
+               }
+
+# check for spacing round square brackets; allowed:
+#  1. with a type on the left -- int [] a;
+#  2. at the beginning of a line for slice initialisers -- [0...10] = 5,
+#  3. inside a curly brace -- = { [0...10] = 5 }
+               while ($line =~ /(.*?\s)\[/g) {
+                       my ($where, $prefix) = ($-[1], $1);
+                       if ($prefix !~ /$Type\s+$/ &&
+                           ($where != 0 || $prefix !~ /^.\s+$/) &&
+                           $prefix !~ /[{,]\s+$/) {
+                               if (ERROR("BRACKET_SPACE",
+                                         "space prohibited before open square bracket '['\n" . $herecurr) &&
+                                   $fix) {
+                                   $fixed[$fixlinenr] =~
+                                       s/^(\+.*?)\s+\[/$1\[/;
+                               }
+                       }
+               }
+
+# check for spaces between functions and their parentheses.
+               while ($line =~ /($Ident)\s+\(/g) {
+                       my $name = $1;
+                       my $ctx_before = substr($line, 0, $-[1]);
+                       my $ctx = "$ctx_before$name";
+
+                       # Ignore those directives where spaces _are_ permitted.
+                       if ($name =~ /^(?:
+                               if|for|while|switch|return|case|
+                               volatile|__volatile__|
+                               __attribute__|format|__extension__|
+                               asm|__asm__)$/x)
+                       {
+                       # cpp #define statements have non-optional spaces, ie
+                       # if there is a space between the name and the open
+                       # parenthesis it is simply not a parameter group.
+                       } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) {
+
+                       # cpp #elif statement condition may start with a (
+                       } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) {
+
+                       # If this whole things ends with a type its most
+                       # likely a typedef for a function.
+                       } elsif ($ctx =~ /$Type$/) {
+
+                       } else {
+                               if (WARN("SPACING",
+                                        "space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
+                                            $fix) {
+                                       $fixed[$fixlinenr] =~
+                                           s/\b$name\s+\(/$name\(/;
+                               }
+                       }
+               }
+
+# Check operator spacing.
+               if (!($line=~/\#\s*include/)) {
+                       my $fixed_line = "";
+                       my $line_fixed = 0;
+
+                       my $ops = qr{
+                               <<=|>>=|<=|>=|==|!=|
+                               \+=|-=|\*=|\/=|%=|\^=|\|=|&=|
+                               =>|->|<<|>>|<|>|=|!|~|
+                               &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
+                               \?:|\?|:
+                       }x;
+                       my @elements = split(/($ops|;)/, $opline);
+
+##                     print("element count: <" . $#elements . ">\n");
+##                     foreach my $el (@elements) {
+##                             print("el: <$el>\n");
+##                     }
+
+                       my @fix_elements = ();
+                       my $off = 0;
+
+                       foreach my $el (@elements) {
+                               push(@fix_elements, substr($rawline, $off, length($el)));
+                               $off += length($el);
+                       }
+
+                       $off = 0;
+
+                       my $blank = copy_spacing($opline);
+                       my $last_after = -1;
+
+                       for (my $n = 0; $n < $#elements; $n += 2) {
+
+                               my $good = $fix_elements[$n] . $fix_elements[$n + 1];
+
+##                             print("n: <$n> good: <$good>\n");
+
+                               $off += length($elements[$n]);
+
+                               # Pick up the preceding and succeeding characters.
+                               my $ca = substr($opline, 0, $off);
+                               my $cc = '';
+                               if (length($opline) >= ($off + length($elements[$n + 1]))) {
+                                       $cc = substr($opline, $off + length($elements[$n + 1]));
+                               }
+                               my $cb = "$ca$;$cc";
+
+                               my $a = '';
+                               $a = 'V' if ($elements[$n] ne '');
+                               $a = 'W' if ($elements[$n] =~ /\s$/);
+                               $a = 'C' if ($elements[$n] =~ /$;$/);
+                               $a = 'B' if ($elements[$n] =~ /(\[|\()$/);
+                               $a = 'O' if ($elements[$n] eq '');
+                               $a = 'E' if ($ca =~ /^\s*$/);
+
+                               my $op = $elements[$n + 1];
+
+                               my $c = '';
+                               if (defined $elements[$n + 2]) {
+                                       $c = 'V' if ($elements[$n + 2] ne '');
+                                       $c = 'W' if ($elements[$n + 2] =~ /^\s/);
+                                       $c = 'C' if ($elements[$n + 2] =~ /^$;/);
+                                       $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
+                                       $c = 'O' if ($elements[$n + 2] eq '');
+                                       $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
+                               } else {
+                                       $c = 'E';
+                               }
+
+                               my $ctx = "${a}x${c}";
+
+                               my $at = "(ctx:$ctx)";
+
+                               my $ptr = substr($blank, 0, $off) . "^";
+                               my $hereptr = "$hereline$ptr\n";
+
+                               # Pull out the value of this operator.
+                               my $op_type = substr($curr_values, $off + 1, 1);
+
+                               # Get the full operator variant.
+                               my $opv = $op . substr($curr_vars, $off, 1);
+
+                               # Ignore operators passed as parameters.
+                               if ($op_type ne 'V' &&
+                                   $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) {
+
+#                              # Ignore comments
+#                              } elsif ($op =~ /^$;+$/) {
+
+                               # ; should have either the end of line or a space or \ after it
+                               } elsif ($op eq ';') {
+                                       if ($ctx !~ /.x[WEBC]/ &&
+                                           $cc !~ /^\\/ && $cc !~ /^;/) {
+                                               if (ERROR("SPACING",
+                                                         "space required after that '$op' $at\n" . $hereptr)) {
+                                                       $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # // is a comment
+                               } elsif ($op eq '//') {
+
+                               #   :   when part of a bitfield
+                               } elsif ($opv eq ':B') {
+                                       # skip the bitfield test for now
+
+                               # No spaces for:
+                               #   ->
+                               } elsif ($op eq '->') {
+                                       if ($ctx =~ /Wx.|.xW/) {
+                                               if (ERROR("SPACING",
+                                                         "spaces prohibited around that '$op' $at\n" . $hereptr)) {
+                                                       $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+                                                       if (defined $fix_elements[$n + 2]) {
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                       }
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # , must not have a space before and must have a space on the right.
+                               } elsif ($op eq ',') {
+                                       my $rtrim_before = 0;
+                                       my $space_after = 0;
+                                       if ($ctx =~ /Wx./) {
+                                               if (ERROR("SPACING",
+                                                         "space prohibited before that '$op' $at\n" . $hereptr)) {
+                                                       $line_fixed = 1;
+                                                       $rtrim_before = 1;
+                                               }
+                                       }
+                                       if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
+                                               if (ERROR("SPACING",
+                                                         "space required after that '$op' $at\n" . $hereptr)) {
+                                                       $line_fixed = 1;
+                                                       $last_after = $n;
+                                                       $space_after = 1;
+                                               }
+                                       }
+                                       if ($rtrim_before || $space_after) {
+                                               if ($rtrim_before) {
+                                                       $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+                                               } else {
+                                                       $good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+                                               }
+                                               if ($space_after) {
+                                                       $good .= " ";
+                                               }
+                                       }
+
+                               # '*' as part of a type definition -- reported already.
+                               } elsif ($opv eq '*_') {
+                                       #warn "'*' is part of type\n";
+
+                               # unary operators should have a space before and
+                               # none after.  May be left adjacent to another
+                               # unary operator, or a cast
+                               } elsif ($op eq '!' || $op eq '~' ||
+                                        $opv eq '*U' || $opv eq '-U' ||
+                                        $opv eq '&U' || $opv eq '&&U') {
+                                       if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
+                                               if (ERROR("SPACING",
+                                                         "space required before that '$op' $at\n" . $hereptr)) {
+                                                       if ($n != $last_after + 2) {
+                                                               $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]);
+                                                               $line_fixed = 1;
+                                                       }
+                                               }
+                                       }
+                                       if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
+                                               # A unary '*' may be const
+
+                                       } elsif ($ctx =~ /.xW/) {
+                                               if (ERROR("SPACING",
+                                                         "space prohibited after that '$op' $at\n" . $hereptr)) {
+                                                       $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]);
+                                                       if (defined $fix_elements[$n + 2]) {
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                       }
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # unary ++ and unary -- are allowed no space on one side.
+                               } elsif ($op eq '++' or $op eq '--') {
+                                       if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) {
+                                               if (ERROR("SPACING",
+                                                         "space required one side of that '$op' $at\n" . $hereptr)) {
+                                                       $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+                                       if ($ctx =~ /Wx[BE]/ ||
+                                           ($ctx =~ /Wx./ && $cc =~ /^;/)) {
+                                               if (ERROR("SPACING",
+                                                         "space prohibited before that '$op' $at\n" . $hereptr)) {
+                                                       $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+                                       if ($ctx =~ /ExW/) {
+                                               if (ERROR("SPACING",
+                                                         "space prohibited after that '$op' $at\n" . $hereptr)) {
+                                                       $good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+                                                       if (defined $fix_elements[$n + 2]) {
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                       }
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # << and >> may either have or not have spaces both sides
+                               } elsif ($op eq '<<' or $op eq '>>' or
+                                        $op eq '&' or $op eq '^' or $op eq '|' or
+                                        $op eq '+' or $op eq '-' or
+                                        $op eq '*' or $op eq '/' or
+                                        $op eq '%')
+                               {
+                                       if ($check) {
+                                               if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) {
+                                                       if (CHK("SPACING",
+                                                               "spaces preferred around that '$op' $at\n" . $hereptr)) {
+                                                               $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                               $line_fixed = 1;
+                                                       }
+                                               } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) {
+                                                       if (CHK("SPACING",
+                                                               "space preferred before that '$op' $at\n" . $hereptr)) {
+                                                               $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]);
+                                                               $line_fixed = 1;
+                                                       }
+                                               }
+                                       } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) {
+                                               if (ERROR("SPACING",
+                                                         "need consistent spacing around '$op' $at\n" . $hereptr)) {
+                                                       $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+                                                       if (defined $fix_elements[$n + 2]) {
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                       }
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # A colon needs no spaces before when it is
+                               # terminating a case value or a label.
+                               } elsif ($opv eq ':C' || $opv eq ':L') {
+                                       if ($ctx =~ /Wx./) {
+                                               if (ERROR("SPACING",
+                                                         "space prohibited before that '$op' $at\n" . $hereptr)) {
+                                                       $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+
+                               # All the others need spaces both sides.
+                               } elsif ($ctx !~ /[EWC]x[CWE]/) {
+                                       my $ok = 0;
+
+                                       # Ignore email addresses <foo@bar>
+                                       if (($op eq '<' &&
+                                            $cc =~ /^\S+\@\S+>/) ||
+                                           ($op eq '>' &&
+                                            $ca =~ /<\S+\@\S+$/))
+                                       {
+                                               $ok = 1;
+                                       }
+
+                                       # for asm volatile statements
+                                       # ignore a colon with another
+                                       # colon immediately before or after
+                                       if (($op eq ':') &&
+                                           ($ca =~ /:$/ || $cc =~ /^:/)) {
+                                               $ok = 1;
+                                       }
+
+                                       # messages are ERROR, but ?: are CHK
+                                       if ($ok == 0) {
+                                               my $msg_level = \&ERROR;
+                                               $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
+
+                                               if (&{$msg_level}("SPACING",
+                                                                 "spaces required around that '$op' $at\n" . $hereptr)) {
+                                                       $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+                                                       if (defined $fix_elements[$n + 2]) {
+                                                               $fix_elements[$n + 2] =~ s/^\s+//;
+                                                       }
+                                                       $line_fixed = 1;
+                                               }
+                                       }
+                               }
+                               $off += length($elements[$n + 1]);
+
+##                             print("n: <$n> GOOD: <$good>\n");
+
+                               $fixed_line = $fixed_line . $good;
+                       }
+
+                       if (($#elements % 2) == 0) {
+                               $fixed_line = $fixed_line . $fix_elements[$#elements];
+                       }
+
+                       if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) {
+                               $fixed[$fixlinenr] = $fixed_line;
+                       }
+
+
+               }
+
+# check for whitespace before a non-naked semicolon
+               if ($line =~ /^\+.*\S\s+;\s*$/) {
+                       if (WARN("SPACING",
+                                "space prohibited before semicolon\n" . $herecurr) &&
+                           $fix) {
+                               1 while $fixed[$fixlinenr] =~
+                                   s/^(\+.*\S)\s+;/$1;/;
+                       }
+               }
+
+# check for multiple assignments
+               if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) {
+                       CHK("MULTIPLE_ASSIGNMENTS",
+                           "multiple assignments should be avoided\n" . $herecurr);
+               }
+
+## # check for multiple declarations, allowing for a function declaration
+## # continuation.
+##             if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ &&
+##                 $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
+##
+##                     # Remove any bracketed sections to ensure we do not
+##                     # falsly report the parameters of functions.
+##                     my $ln = $line;
+##                     while ($ln =~ s/\([^\(\)]*\)//g) {
+##                     }
+##                     if ($ln =~ /,/) {
+##                             WARN("MULTIPLE_DECLARATION",
+##                                  "declaring multiple variables together should be avoided\n" . $herecurr);
+##                     }
+##             }
+
+#need space before brace following if, while, etc
+               if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) ||
+                   $line =~ /do\{/) {
+                       if (ERROR("SPACING",
+                                 "space required before the open brace '{'\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/;
+                       }
+               }
+
+## # check for blank lines before declarations
+##             if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ &&
+##                 $prevrawline =~ /^.\s*$/) {
+##                     WARN("SPACING",
+##                          "No blank lines before declarations\n" . $hereprev);
+##             }
+##
+
+# closing brace should have a space following it when it has anything
+# on the line
+               if ($line =~ /}(?!(?:,|;|\)))\S/) {
+                       if (ERROR("SPACING",
+                                 "space required after that close brace '}'\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/}((?!(?:,|;|\)))\S)/} $1/;
+                       }
+               }
+
+# check spacing on square brackets
+               if ($line =~ /\[\s/ && $line !~ /\[\s*$/) {
+                       if (ERROR("SPACING",
+                                 "space prohibited after that open square bracket '['\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\[\s+/\[/;
+                       }
+               }
+               if ($line =~ /\s\]/) {
+                       if (ERROR("SPACING",
+                                 "space prohibited before that close square bracket ']'\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\s+\]/\]/;
+                       }
+               }
+
+# check spacing on parentheses
+               if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ &&
+                   $line !~ /for\s*\(\s+;/) {
+                       if (ERROR("SPACING",
+                                 "space prohibited after that open parenthesis '('\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\(\s+/\(/;
+                       }
+               }
+               if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ &&
+                   $line !~ /for\s*\(.*;\s+\)/ &&
+                   $line !~ /:\s+\)/) {
+                       if (ERROR("SPACING",
+                                 "space prohibited before that close parenthesis ')'\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\s+\)/\)/;
+                       }
+               }
+
+# check unnecessary parentheses around addressof/dereference single $Lvals
+# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar
+
+               while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) {
+                       my $var = $1;
+                       if (CHK("UNNECESSARY_PARENTHESES",
+                               "Unnecessary parentheses around $var\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/;
+                       }
+               }
+
+# check for unnecessary parentheses around function pointer uses
+# ie: (foo->bar)(); should be foo->bar();
+# but not "if (foo->bar) (" to avoid some false positives
+               if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) {
+                       my $var = $2;
+                       if (CHK("UNNECESSARY_PARENTHESES",
+                               "Unnecessary parentheses around function pointer $var\n" . $herecurr) &&
+                           $fix) {
+                               my $var2 = deparenthesize($var);
+                               $var2 =~ s/\s//g;
+                               $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/;
+                       }
+               }
+
+# check for unnecessary parentheses around comparisons in if uses
+               if ($^V && $^V ge 5.10.0 && defined($stat) &&
+                   $stat =~ /(^.\s*if\s*($balanced_parens))/) {
+                       my $if_stat = $1;
+                       my $test = substr($2, 1, -1);
+                       my $herectx;
+                       while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) {
+                               my $match = $1;
+                               # avoid parentheses around potential macro args
+                               next if ($match =~ /^\s*\w+\s*$/);
+                               if (!defined($herectx)) {
+                                       $herectx = $here . "\n";
+                                       my $cnt = statement_rawlines($if_stat);
+                                       for (my $n = 0; $n < $cnt; $n++) {
+                                               my $rl = raw_line($linenr, $n);
+                                               $herectx .=  $rl . "\n";
+                                               last if $rl =~ /^[ \+].*\{/;
+                                       }
+                               }
+                               CHK("UNNECESSARY_PARENTHESES",
+                                   "Unnecessary parentheses around '$match'\n" . $herectx);
+                       }
+               }
+
+#goto labels aren't indented, allow a single space however
+               if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and
+                  !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) {
+                       if (WARN("INDENTED_LABEL",
+                                "labels should not be indented\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/^(.)\s+/$1/;
+                       }
+               }
+
+# return is not a function
+               if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
+                       my $spacing = $1;
+                       if ($^V && $^V ge 5.10.0 &&
+                           $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
+                               my $value = $1;
+                               $value = deparenthesize($value);
+                               if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) {
+                                       ERROR("RETURN_PARENTHESES",
+                                             "return is not a function, parentheses are not required\n" . $herecurr);
+                               }
+                       } elsif ($spacing !~ /\s+/) {
+                               ERROR("SPACING",
+                                     "space required before the open parenthesis '('\n" . $herecurr);
+                       }
+               }
+
+# unnecessary return in a void function
+# at end-of-function, with the previous line a single leading tab, then return;
+# and the line before that not a goto label target like "out:"
+               if ($sline =~ /^[ \+]}\s*$/ &&
+                   $prevline =~ /^\+\treturn\s*;\s*$/ &&
+                   $linenr >= 3 &&
+                   $lines[$linenr - 3] =~ /^[ +]/ &&
+                   $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) {
+                       WARN("RETURN_VOID",
+                            "void function return statements are not generally useful\n" . $hereprev);
+               }
+
+# if statements using unnecessary parentheses - ie: if ((foo == bar))
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /\bif\s*((?:\(\s*){2,})/) {
+                       my $openparens = $1;
+                       my $count = $openparens =~ tr@\(@\(@;
+                       my $msg = "";
+                       if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) {
+                               my $comp = $4;  #Not $1 because of $LvalOrFunc
+                               $msg = " - maybe == should be = ?" if ($comp eq "==");
+                               WARN("UNNECESSARY_PARENTHESES",
+                                    "Unnecessary parentheses$msg\n" . $herecurr);
+                       }
+               }
+
+# comparisons with a constant or upper case identifier on the left
+#      avoid cases like "foo + BAR < baz"
+#      only fix matches surrounded by parentheses to avoid incorrect
+#      conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5"
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) {
+                       my $lead = $1;
+                       my $const = $2;
+                       my $comp = $3;
+                       my $to = $4;
+                       my $newcomp = $comp;
+                       if ($lead !~ /(?:$Operators|\.)\s*$/ &&
+                           $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ &&
+                           WARN("CONSTANT_COMPARISON",
+                                "Comparisons should place the constant on the right side of the test\n" . $herecurr) &&
+                           $fix) {
+                               if ($comp eq "<") {
+                                       $newcomp = ">";
+                               } elsif ($comp eq "<=") {
+                                       $newcomp = ">=";
+                               } elsif ($comp eq ">") {
+                                       $newcomp = "<";
+                               } elsif ($comp eq ">=") {
+                                       $newcomp = "<=";
+                               }
+                               $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/;
+                       }
+               }
+
+# Return of what appears to be an errno should normally be negative
+               if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) {
+                       my $name = $1;
+                       if ($name ne 'EOF' && $name ne 'ERROR') {
+                               WARN("USE_NEGATIVE_ERRNO",
+                                    "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr);
+                       }
+               }
+
+# Need a space before open parenthesis after if, while etc
+               if ($line =~ /\b(if|while|for|switch)\(/) {
+                       if (ERROR("SPACING",
+                                 "space required before the open parenthesis '('\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/\b(if|while|for|switch)\(/$1 \(/;
+                       }
+               }
+
+# Check for illegal assignment in if conditional -- and check for trailing
+# statements after the conditional.
+               if ($line =~ /do\s*(?!{)/) {
+                       ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+                               ctx_statement_block($linenr, $realcnt, 0)
+                                       if (!defined $stat);
+                       my ($stat_next) = ctx_statement_block($line_nr_next,
+                                               $remain_next, $off_next);
+                       $stat_next =~ s/\n./\n /g;
+                       ##print "stat<$stat> stat_next<$stat_next>\n";
+
+                       if ($stat_next =~ /^\s*while\b/) {
+                               # If the statement carries leading newlines,
+                               # then count those as offsets.
+                               my ($whitespace) =
+                                       ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
+                               my $offset =
+                                       statement_rawlines($whitespace) - 1;
+
+                               $suppress_whiletrailers{$line_nr_next +
+                                                               $offset} = 1;
+                       }
+               }
+               if (!defined $suppress_whiletrailers{$linenr} &&
+                   defined($stat) && defined($cond) &&
+                   $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
+                       my ($s, $c) = ($stat, $cond);
+
+                       if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
+                               ERROR("ASSIGN_IN_IF",
+                                     "do not use assignment in if condition\n" . $herecurr);
+                       }
+
+                       # Find out what is on the end of the line after the
+                       # conditional.
+                       substr($s, 0, length($c), '');
+                       $s =~ s/\n.*//g;
+                       $s =~ s/$;//g;  # Remove any comments
+                       if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
+                           $c !~ /}\s*while\s*/)
+                       {
+                               # Find out how long the conditional actually is.
+                               my @newlines = ($c =~ /\n/gs);
+                               my $cond_lines = 1 + $#newlines;
+                               my $stat_real = '';
+
+                               $stat_real = raw_line($linenr, $cond_lines)
+                                                       . "\n" if ($cond_lines);
+                               if (defined($stat_real) && $cond_lines > 1) {
+                                       $stat_real = "[...]\n$stat_real";
+                               }
+
+                               ERROR("TRAILING_STATEMENTS",
+                                     "trailing statements should be on next line\n" . $herecurr . $stat_real);
+                       }
+               }
+
+# Check for bitwise tests written as boolean
+               if ($line =~ /
+                       (?:
+                               (?:\[|\(|\&\&|\|\|)
+                               \s*0[xX][0-9]+\s*
+                               (?:\&\&|\|\|)
+                       |
+                               (?:\&\&|\|\|)
+                               \s*0[xX][0-9]+\s*
+                               (?:\&\&|\|\||\)|\])
+                       )/x)
+               {
+                       WARN("HEXADECIMAL_BOOLEAN_TEST",
+                            "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr);
+               }
+
+# if and else should not have general statements after it
+               if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
+                       my $s = $1;
+                       $s =~ s/$;//g;  # Remove any comments
+                       if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
+                               ERROR("TRAILING_STATEMENTS",
+                                     "trailing statements should be on next line\n" . $herecurr);
+                       }
+               }
+# if should not continue a brace
+               if ($line =~ /}\s*if\b/) {
+                       ERROR("TRAILING_STATEMENTS",
+                             "trailing statements should be on next line (or did you mean 'else if'?)\n" .
+                               $herecurr);
+               }
+# case and default should not have general statements after them
+               if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
+                   $line !~ /\G(?:
+                       (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
+                       \s*return\s+
+                   )/xg)
+               {
+                       ERROR("TRAILING_STATEMENTS",
+                             "trailing statements should be on next line\n" . $herecurr);
+               }
+
+               # Check for }<nl>else {, these must be at the same
+               # indent level to be relevant to each other.
+               if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ &&
+                   $previndent == $indent) {
+                       if (ERROR("ELSE_AFTER_BRACE",
+                                 "else should follow close brace '}'\n" . $hereprev) &&
+                           $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                               fix_delete_line($fixlinenr, $rawline);
+                               my $fixedline = $prevrawline;
+                               $fixedline =~ s/}\s*$//;
+                               if ($fixedline !~ /^\+\s*$/) {
+                                       fix_insert_line($fixlinenr, $fixedline);
+                               }
+                               $fixedline = $rawline;
+                               $fixedline =~ s/^(.\s*)else/$1} else/;
+                               fix_insert_line($fixlinenr, $fixedline);
+                       }
+               }
+
+               if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ &&
+                   $previndent == $indent) {
+                       my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0);
+
+                       # Find out what is on the end of the line after the
+                       # conditional.
+                       substr($s, 0, length($c), '');
+                       $s =~ s/\n.*//g;
+
+                       if ($s =~ /^\s*;/) {
+                               if (ERROR("WHILE_AFTER_BRACE",
+                                         "while should follow close brace '}'\n" . $hereprev) &&
+                                   $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+                                       fix_delete_line($fixlinenr - 1, $prevrawline);
+                                       fix_delete_line($fixlinenr, $rawline);
+                                       my $fixedline = $prevrawline;
+                                       my $trailing = $rawline;
+                                       $trailing =~ s/^\+//;
+                                       $trailing = trim($trailing);
+                                       $fixedline =~ s/}\s*$/} $trailing/;
+                                       fix_insert_line($fixlinenr, $fixedline);
+                               }
+                       }
+               }
+
+#Specific variable tests
+               while ($line =~ m{($Constant|$Lval)}g) {
+                       my $var = $1;
+
+#gcc binary extension
+                       if ($var =~ /^$Binary$/) {
+                               if (WARN("GCC_BINARY_CONSTANT",
+                                        "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) &&
+                                   $fix) {
+                                       my $hexval = sprintf("0x%x", oct($var));
+                                       $fixed[$fixlinenr] =~
+                                           s/\b$var\b/$hexval/;
+                               }
+                       }
+
+#CamelCase
+                       if ($var !~ /^$Constant$/ &&
+                           $var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore Page<foo> variants
+                           $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ &&
+#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show)
+                           $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ &&
+#Ignore some three character SI units explicitly, like MiB and KHz
+                           $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
+                               while ($var =~ m{($Ident)}g) {
+                                       my $word = $1;
+                                       next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/);
+                                       if ($check) {
+                                               seed_camelcase_includes();
+                                               if (!$file && !$camelcase_file_seeded) {
+                                                       seed_camelcase_file($realfile);
+                                                       $camelcase_file_seeded = 1;
+                                               }
+                                       }
+                                       if (!defined $camelcase{$word}) {
+                                               $camelcase{$word} = 1;
+                                               CHK("CAMELCASE",
+                                                   "Avoid CamelCase: <$word>\n" . $herecurr);
+                                       }
+                               }
+                       }
+               }
+
+#no spaces allowed after \ in define
+               if ($line =~ /\#\s*define.*\\\s+$/) {
+                       if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION",
+                                "Whitespace after \\ makes next lines useless\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\s+$//;
+                       }
+               }
+
+# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes
+# itself <asm/foo.h> (uses RAW line)
+               if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) {
+                       my $file = "$1.h";
+                       my $checkfile = "include/linux/$file";
+                       if (-f "$root/$checkfile" &&
+                           $realfile ne $checkfile &&
+                           $1 !~ /$allowed_asm_includes/)
+                       {
+                               my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`;
+                               if ($asminclude > 0) {
+                                       if ($realfile =~ m{^arch/}) {
+                                               CHK("ARCH_INCLUDE_LINUX",
+                                                   "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+                                       } else {
+                                               WARN("INCLUDE_LINUX",
+                                                    "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+                                       }
+                               }
+                       }
+               }
+
+# multi-statement macros should be enclosed in a do while loop, grab the
+# first statement and ensure its the whole macro if its not enclosed
+# in a known good container
+               if ($realfile !~ m@/vmlinux.lds.h$@ &&
+                   $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
+                       my $ln = $linenr;
+                       my $cnt = $realcnt;
+                       my ($off, $dstat, $dcond, $rest);
+                       my $ctx = '';
+                       my $has_flow_statement = 0;
+                       my $has_arg_concat = 0;
+                       ($dstat, $dcond, $ln, $cnt, $off) =
+                               ctx_statement_block($linenr, $realcnt, 0);
+                       $ctx = $dstat;
+                       #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
+                       #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
+
+                       $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/);
+                       $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/);
+
+                       $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//;
+                       my $define_args = $1;
+                       my $define_stmt = $dstat;
+                       my @def_args = ();
+
+                       if (defined $define_args && $define_args ne "") {
+                               $define_args = substr($define_args, 1, length($define_args) - 2);
+                               $define_args =~ s/\s*//g;
+                               @def_args = split(",", $define_args);
+                       }
+
+                       $dstat =~ s/$;//g;
+                       $dstat =~ s/\\\n.//g;
+                       $dstat =~ s/^\s*//s;
+                       $dstat =~ s/\s*$//s;
+
+                       # Flatten any parentheses and braces
+                       while ($dstat =~ s/\([^\(\)]*\)/1/ ||
+                              $dstat =~ s/\{[^\{\}]*\}/1/ ||
+                              $dstat =~ s/.\[[^\[\]]*\]/1/)
+                       {
+                       }
+
+                       # Flatten any obvious string concatentation.
+                       while ($dstat =~ s/($String)\s*$Ident/$1/ ||
+                              $dstat =~ s/$Ident\s*($String)/$1/)
+                       {
+                       }
+
+                       # Make asm volatile uses seem like a generic function
+                       $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g;
+
+                       my $exceptions = qr{
+                               $Declare|
+                               module_param_named|
+                               MODULE_PARM_DESC|
+                               DECLARE_PER_CPU|
+                               DEFINE_PER_CPU|
+                               __typeof__\(|
+                               union|
+                               struct|
+                               \.$Ident\s*=\s*|
+                               ^\"|\"$|
+                               ^\[
+                       }x;
+                       #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+
+                       $ctx =~ s/\n*$//;
+                       my $herectx = $here . "\n";
+                       my $stmt_cnt = statement_rawlines($ctx);
+
+                       for (my $n = 0; $n < $stmt_cnt; $n++) {
+                               $herectx .= raw_line($linenr, $n) . "\n";
+                       }
+
+                       if ($dstat ne '' &&
+                           $dstat !~ /^(?:$Ident|-?$Constant),$/ &&                    # 10, // foo(),
+                           $dstat !~ /^(?:$Ident|-?$Constant);$/ &&                    # foo();
+                           $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ &&          # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz
+                           $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ &&                  # character constants
+                           $dstat !~ /$exceptions/ &&
+                           $dstat !~ /^\.$Ident\s*=/ &&                                # .foo =
+                           $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ &&          # stringification #foo
+                           $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ &&       # do {...} while (...); // do {...} while (...)
+                           $dstat !~ /^for\s*$Constant$/ &&                            # for (...)
+                           $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ &&   # for (...) bar()
+                           $dstat !~ /^do\s*{/ &&                                      # do {...
+                           $dstat !~ /^\(\{/ &&                                                # ({...
+                           $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
+                       {
+                               if ($dstat =~ /^\s*if\b/) {
+                                       ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+                                             "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
+                               } elsif ($dstat =~ /;/) {
+                                       ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+                                             "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
+                               } else {
+                                       ERROR("COMPLEX_MACRO",
+                                             "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
+                               }
+
+                       }
+
+                       # Make $define_stmt single line, comment-free, etc
+                       my @stmt_array = split('\n', $define_stmt);
+                       my $first = 1;
+                       $define_stmt = "";
+                       foreach my $l (@stmt_array) {
+                               $l =~ s/\\$//;
+                               if ($first) {
+                                       $define_stmt = $l;
+                                       $first = 0;
+                               } elsif ($l =~ /^[\+ ]/) {
+                                       $define_stmt .= substr($l, 1);
+                               }
+                       }
+                       $define_stmt =~ s/$;//g;
+                       $define_stmt =~ s/\s+/ /g;
+                       $define_stmt = trim($define_stmt);
+
+# check if any macro arguments are reused (ignore '...' and 'type')
+                       foreach my $arg (@def_args) {
+                               next if ($arg =~ /\.\.\./);
+                               next if ($arg =~ /^type$/i);
+                               my $tmp_stmt = $define_stmt;
+                               $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+                               $tmp_stmt =~ s/\#+\s*$arg\b//g;
+                               $tmp_stmt =~ s/\b$arg\s*\#\#//g;
+                               my $use_cnt = $tmp_stmt =~ s/\b$arg\b//g;
+                               if ($use_cnt > 1) {
+                                       CHK("MACRO_ARG_REUSE",
+                                           "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx");
+                                   }
+# check if any macro arguments may have other precedence issues
+                               if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m &&
+                                   ((defined($1) && $1 ne ',') ||
+                                    (defined($2) && $2 ne ','))) {
+                                       CHK("MACRO_ARG_PRECEDENCE",
+                                           "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
+                               }
+                       }
+
+# check for macros with flow control, but without ## concatenation
+# ## concatenation is commonly a macro that defines a function so ignore those
+                       if ($has_flow_statement && !$has_arg_concat) {
+                               my $herectx = $here . "\n";
+                               my $cnt = statement_rawlines($ctx);
+
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+                               WARN("MACRO_WITH_FLOW_CONTROL",
+                                    "Macros with flow control statements should be avoided\n" . "$herectx");
+                       }
+
+# check for line continuations outside of #defines, preprocessor #, and asm
+
+               } else {
+                       if ($prevline !~ /^..*\\$/ &&
+                           $line !~ /^\+\s*\#.*\\$/ &&         # preprocessor
+                           $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ &&   # asm
+                           $line =~ /^\+.*\\$/) {
+                               WARN("LINE_CONTINUATIONS",
+                                    "Avoid unnecessary line continuations\n" . $herecurr);
+                       }
+               }
+
+# do {} while (0) macro tests:
+# single-statement macros do not need to be enclosed in do while (0) loop,
+# macro should not end with a semicolon
+               if ($^V && $^V ge 5.10.0 &&
+                   $realfile !~ m@/vmlinux.lds.h$@ &&
+                   $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
+                       my $ln = $linenr;
+                       my $cnt = $realcnt;
+                       my ($off, $dstat, $dcond, $rest);
+                       my $ctx = '';
+                       ($dstat, $dcond, $ln, $cnt, $off) =
+                               ctx_statement_block($linenr, $realcnt, 0);
+                       $ctx = $dstat;
+
+                       $dstat =~ s/\\\n.//g;
+                       $dstat =~ s/$;/ /g;
+
+                       if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) {
+                               my $stmts = $2;
+                               my $semis = $3;
+
+                               $ctx =~ s/\n*$//;
+                               my $cnt = statement_rawlines($ctx);
+                               my $herectx = $here . "\n";
+
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+
+                               if (($stmts =~ tr/;/;/) == 1 &&
+                                   $stmts !~ /^\s*(if|while|for|switch)\b/) {
+                                       WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
+                                            "Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
+                               }
+                               if (defined $semis && $semis ne "") {
+                                       WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON",
+                                            "do {} while (0) macros should not be semicolon terminated\n" . "$herectx");
+                               }
+                       } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) {
+                               $ctx =~ s/\n*$//;
+                               my $cnt = statement_rawlines($ctx);
+                               my $herectx = $here . "\n";
+
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+
+                               WARN("TRAILING_SEMICOLON",
+                                    "macros should not use a trailing semicolon\n" . "$herectx");
+                       }
+               }
+
+# make sure symbols are always wrapped with VMLINUX_SYMBOL() ...
+# all assignments may have only one of the following with an assignment:
+#      .
+#      ALIGN(...)
+#      VMLINUX_SYMBOL(...)
+               if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) {
+                       WARN("MISSING_VMLINUX_SYMBOL",
+                            "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr);
+               }
+
+# check for redundant bracing round if etc
+               if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) {
+                       my ($level, $endln, @chunks) =
+                               ctx_statement_full($linenr, $realcnt, 1);
+                       #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n";
+                       #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n";
+                       if ($#chunks > 0 && $level == 0) {
+                               my @allowed = ();
+                               my $allow = 0;
+                               my $seen = 0;
+                               my $herectx = $here . "\n";
+                               my $ln = $linenr - 1;
+                               for my $chunk (@chunks) {
+                                       my ($cond, $block) = @{$chunk};
+
+                                       # If the condition carries leading newlines, then count those as offsets.
+                                       my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s);
+                                       my $offset = statement_rawlines($whitespace) - 1;
+
+                                       $allowed[$allow] = 0;
+                                       #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n";
+
+                                       # We have looked at and allowed this specific line.
+                                       $suppress_ifbraces{$ln + $offset} = 1;
+
+                                       $herectx .= "$rawlines[$ln + $offset]\n[...]\n";
+                                       $ln += statement_rawlines($block) - 1;
+
+                                       substr($block, 0, length($cond), '');
+
+                                       $seen++ if ($block =~ /^\s*{/);
+
+                                       #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n";
+                                       if (statement_lines($cond) > 1) {
+                                               #print "APW: ALLOWED: cond<$cond>\n";
+                                               $allowed[$allow] = 1;
+                                       }
+                                       if ($block =~/\b(?:if|for|while)\b/) {
+                                               #print "APW: ALLOWED: block<$block>\n";
+                                               $allowed[$allow] = 1;
+                                       }
+                                       if (statement_block_size($block) > 1) {
+                                               #print "APW: ALLOWED: lines block<$block>\n";
+                                               $allowed[$allow] = 1;
+                                       }
+                                       $allow++;
+                               }
+                               if ($seen) {
+                                       my $sum_allowed = 0;
+                                       foreach (@allowed) {
+                                               $sum_allowed += $_;
+                                       }
+                                       if ($sum_allowed == 0) {
+                                               WARN("BRACES",
+                                                    "braces {} are not necessary for any arm of this statement\n" . $herectx);
+                                       } elsif ($sum_allowed != $allow &&
+                                                $seen != $allow) {
+                                               CHK("BRACES",
+                                                   "braces {} should be used on all arms of this statement\n" . $herectx);
+                                       }
+                               }
+                       }
+               }
+               if (!defined $suppress_ifbraces{$linenr - 1} &&
+                                       $line =~ /\b(if|while|for|else)\b/) {
+                       my $allowed = 0;
+
+                       # Check the pre-context.
+                       if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) {
+                               #print "APW: ALLOWED: pre<$1>\n";
+                               $allowed = 1;
+                       }
+
+                       my ($level, $endln, @chunks) =
+                               ctx_statement_full($linenr, $realcnt, $-[0]);
+
+                       # Check the condition.
+                       my ($cond, $block) = @{$chunks[0]};
+                       #print "CHECKING<$linenr> cond<$cond> block<$block>\n";
+                       if (defined $cond) {
+                               substr($block, 0, length($cond), '');
+                       }
+                       if (statement_lines($cond) > 1) {
+                               #print "APW: ALLOWED: cond<$cond>\n";
+                               $allowed = 1;
+                       }
+                       if ($block =~/\b(?:if|for|while)\b/) {
+                               #print "APW: ALLOWED: block<$block>\n";
+                               $allowed = 1;
+                       }
+                       if (statement_block_size($block) > 1) {
+                               #print "APW: ALLOWED: lines block<$block>\n";
+                               $allowed = 1;
+                       }
+                       # Check the post-context.
+                       if (defined $chunks[1]) {
+                               my ($cond, $block) = @{$chunks[1]};
+                               if (defined $cond) {
+                                       substr($block, 0, length($cond), '');
+                               }
+                               if ($block =~ /^\s*\{/) {
+                                       #print "APW: ALLOWED: chunk-1 block<$block>\n";
+                                       $allowed = 1;
+                               }
+                       }
+                       if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) {
+                               my $herectx = $here . "\n";
+                               my $cnt = statement_rawlines($block);
+
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+
+                               WARN("BRACES",
+                                    "braces {} are not necessary for single statement blocks\n" . $herectx);
+                       }
+               }
+
+# check for single line unbalanced braces
+               if ($sline =~ /^.\s*\}\s*else\s*$/ ||
+                   $sline =~ /^.\s*else\s*\{\s*$/) {
+                       CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr);
+               }
+
+# check for unnecessary blank lines around braces
+               if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) {
+                       if (CHK("BRACES",
+                               "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) &&
+                           $fix && $prevrawline =~ /^\+/) {
+                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                       }
+               }
+               if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) {
+                       if (CHK("BRACES",
+                               "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) &&
+                           $fix) {
+                               fix_delete_line($fixlinenr, $rawline);
+                       }
+               }
+
+# no volatiles please
+               my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b};
+               if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) {
+                       WARN("VOLATILE",
+                            "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr);
+               }
+
+# Check for user-visible strings broken across lines, which breaks the ability
+# to grep for the string.  Make exceptions when the previous string ends in a
+# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{'
+# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value
+               if ($line =~ /^\+\s*$String/ &&
+                   $prevline =~ /"\s*$/ &&
+                   $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) {
+                       if (WARN("SPLIT_STRING",
+                                "quoted string split across lines\n" . $hereprev) &&
+                                    $fix &&
+                                    $prevrawline =~ /^\+.*"\s*$/ &&
+                                    $last_coalesced_string_linenr != $linenr - 1) {
+                               my $extracted_string = get_quoted_string($line, $rawline);
+                               my $comma_close = "";
+                               if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) {
+                                       $comma_close = $1;
+                               }
+
+                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                               fix_delete_line($fixlinenr, $rawline);
+                               my $fixedline = $prevrawline;
+                               $fixedline =~ s/"\s*$//;
+                               $fixedline .= substr($extracted_string, 1) . trim($comma_close);
+                               fix_insert_line($fixlinenr - 1, $fixedline);
+                               $fixedline = $rawline;
+                               $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//;
+                               if ($fixedline !~ /\+\s*$/) {
+                                       fix_insert_line($fixlinenr, $fixedline);
+                               }
+                               $last_coalesced_string_linenr = $linenr;
+                       }
+               }
+
+# check for missing a space in a string concatenation
+               if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) {
+                       WARN('MISSING_SPACE',
+                            "break quoted strings at a space character\n" . $hereprev);
+               }
+
+# check for an embedded function name in a string when the function is known
+# This does not work very well for -f --file checking as it depends on patch
+# context providing the function name or a single line form for in-file
+# function declarations
+               if ($line =~ /^\+.*$String/ &&
+                   defined($context_function) &&
+                   get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
+                   length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
+                       WARN("EMBEDDED_FUNCTION_NAME",
+                            "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
+               }
+
+# check for spaces before a quoted newline
+               if ($rawline =~ /^.*\".*\s\\n/) {
+                       if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
+                                "unnecessary whitespace before a quoted newline\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/;
+                       }
+
+               }
+
+# concatenated string without spaces between elements
+               if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) {
+                       CHK("CONCATENATED_STRING",
+                           "Concatenated strings should use spaces between elements\n" . $herecurr);
+               }
+
+# uncoalesced string fragments
+               if ($line =~ /$String\s*"/) {
+                       WARN("STRING_FRAGMENTS",
+                            "Consecutive strings are generally better as a single string\n" . $herecurr);
+               }
+
+# check for non-standard and hex prefixed decimal printf formats
+               my $show_L = 1; #don't show the same defect twice
+               my $show_Z = 1;
+               while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
+                       my $string = substr($rawline, $-[1], $+[1] - $-[1]);
+                       $string =~ s/%%/__/g;
+                       # check for %L
+                       if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) {
+                               WARN("PRINTF_L",
+                                    "\%L$1 is non-standard C, use %ll$1\n" . $herecurr);
+                               $show_L = 0;
+                       }
+                       # check for %Z
+                       if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) {
+                               WARN("PRINTF_Z",
+                                    "%Z$1 is non-standard C, use %z$1\n" . $herecurr);
+                               $show_Z = 0;
+                       }
+                       # check for 0x<decimal>
+                       if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) {
+                               ERROR("PRINTF_0XDECIMAL",
+                                     "Prefixing 0x with decimal output is defective\n" . $herecurr);
+                       }
+               }
+
+# check for line continuations in quoted strings with odd counts of "
+               if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) {
+                       WARN("LINE_CONTINUATIONS",
+                            "Avoid line continuations in quoted strings\n" . $herecurr);
+               }
+
+# warn about #if 0
+               if ($line =~ /^.\s*\#\s*if\s+0\b/) {
+                       CHK("REDUNDANT_CODE",
+                           "if this code is redundant consider removing it\n" .
+                               $herecurr);
+               }
+
+# check for needless "if (<foo>) fn(<foo>)" uses
+               if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) {
+                       my $tested = quotemeta($1);
+                       my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;';
+                       if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) {
+                               my $func = $1;
+                               if (WARN('NEEDLESS_IF',
+                                        "$func(NULL) is safe and this check is probably not required\n" . $hereprev) &&
+                                   $fix) {
+                                       my $do_fix = 1;
+                                       my $leading_tabs = "";
+                                       my $new_leading_tabs = "";
+                                       if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) {
+                                               $leading_tabs = $1;
+                                       } else {
+                                               $do_fix = 0;
+                                       }
+                                       if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) {
+                                               $new_leading_tabs = $1;
+                                               if (length($leading_tabs) + 1 ne length($new_leading_tabs)) {
+                                                       $do_fix = 0;
+                                               }
+                                       } else {
+                                               $do_fix = 0;
+                                       }
+                                       if ($do_fix) {
+                                               fix_delete_line($fixlinenr - 1, $prevrawline);
+                                               $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/;
+                                       }
+                               }
+                       }
+               }
+
+# check for unnecessary "Out of Memory" messages
+               if ($line =~ /^\+.*\b$logFunctions\s*\(/ &&
+                   $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ &&
+                   (defined $1 || defined $3) &&
+                   $linenr > 3) {
+                       my $testval = $2;
+                       my $testline = $lines[$linenr - 3];
+
+                       my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0);
+#                      print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n");
+
+                       if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) {
+                               WARN("OOM_MESSAGE",
+                                    "Possible unnecessary 'out of memory' message\n" . $hereprev);
+                       }
+               }
+
+# check for logging functions with KERN_<LEVEL>
+               if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ &&
+                   $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) {
+                       my $level = $1;
+                       if (WARN("UNNECESSARY_KERN_LEVEL",
+                                "Possible unnecessary $level\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\s*$level\s*//;
+                       }
+               }
+
+# check for logging continuations
+               if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) {
+                       WARN("LOGGING_CONTINUATION",
+                            "Avoid logging continuation uses where feasible\n" . $herecurr);
+               }
+
+# check for mask then right shift without a parentheses
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ &&
+                   $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so
+                       WARN("MASK_THEN_SHIFT",
+                            "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr);
+               }
+
+# check for pointer comparisons to NULL
+               if ($^V && $^V ge 5.10.0) {
+                       while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) {
+                               my $val = $1;
+                               my $equal = "!";
+                               $equal = "" if ($4 eq "!=");
+                               if (CHK("COMPARISON_TO_NULL",
+                                       "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) &&
+                                           $fix) {
+                                       $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/;
+                               }
+                       }
+               }
+
+# check for bad placement of section $InitAttribute (e.g.: __initdata)
+               if ($line =~ /(\b$InitAttribute\b)/) {
+                       my $attr = $1;
+                       if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) {
+                               my $ptr = $1;
+                               my $var = $2;
+                               if ((($ptr =~ /\b(union|struct)\s+$attr\b/ &&
+                                     ERROR("MISPLACED_INIT",
+                                           "$attr should be placed after $var\n" . $herecurr)) ||
+                                    ($ptr !~ /\b(union|struct)\s+$attr\b/ &&
+                                     WARN("MISPLACED_INIT",
+                                          "$attr should be placed after $var\n" . $herecurr))) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e;
+                               }
+                       }
+               }
+
+# check for $InitAttributeData (ie: __initdata) with const
+               if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) {
+                       my $attr = $1;
+                       $attr =~ /($InitAttributePrefix)(.*)/;
+                       my $attr_prefix = $1;
+                       my $attr_type = $2;
+                       if (ERROR("INIT_ATTRIBUTE",
+                                 "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/$InitAttributeData/${attr_prefix}initconst/;
+                       }
+               }
+
+# check for $InitAttributeConst (ie: __initconst) without const
+               if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) {
+                       my $attr = $1;
+                       if (ERROR("INIT_ATTRIBUTE",
+                                 "Use of $attr requires a separate use of const\n" . $herecurr) &&
+                           $fix) {
+                               my $lead = $fixed[$fixlinenr] =~
+                                   /(^\+\s*(?:static\s+))/;
+                               $lead = rtrim($1);
+                               $lead = "$lead " if ($lead !~ /^\+$/);
+                               $lead = "${lead}const ";
+                               $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/;
+                       }
+               }
+
+# check for __read_mostly with const non-pointer (should just be const)
+               if ($line =~ /\b__read_mostly\b/ &&
+                   $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) {
+                       if (ERROR("CONST_READ_MOSTLY",
+                                 "Invalid use of __read_mostly with const type\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//;
+                       }
+               }
+
+# don't use __constant_<foo> functions outside of include/uapi/
+               if ($realfile !~ m@^include/uapi/@ &&
+                   $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) {
+                       my $constant_func = $1;
+                       my $func = $constant_func;
+                       $func =~ s/^__constant_//;
+                       if (WARN("CONSTANT_CONVERSION",
+                                "$constant_func should be $func\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g;
+                       }
+               }
+
+# prefer usleep_range over udelay
+               if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) {
+                       my $delay = $1;
+                       # ignore udelay's < 10, however
+                       if (! ($delay < 10) ) {
+                               CHK("USLEEP_RANGE",
+                                   "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+                       }
+                       if ($delay > 2000) {
+                               WARN("LONG_UDELAY",
+                                    "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
+                       }
+               }
+
+# warn about unexpectedly long msleep's
+               if ($line =~ /\bmsleep\s*\((\d+)\);/) {
+                       if ($1 < 20) {
+                               WARN("MSLEEP",
+                                    "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr);
+                       }
+               }
+
+# check for comparisons of jiffies
+               if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) {
+                       WARN("JIFFIES_COMPARISON",
+                            "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr);
+               }
+
+# check for comparisons of get_jiffies_64()
+               if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) {
+                       WARN("JIFFIES_COMPARISON",
+                            "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr);
+               }
+
+# warn about #ifdefs in C files
+#              if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) {
+#                      print "#ifdef in C files should be avoided\n";
+#                      print "$herecurr";
+#                      $clean = 0;
+#              }
+
+# warn about spacing in #ifdefs
+               if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) {
+                       if (ERROR("SPACING",
+                                 "exactly one space required after that #$1\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~
+                                   s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /;
+                       }
+
+               }
+
+# check for spinlock_t definitions without a comment.
+               if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ ||
+                   $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) {
+                       my $which = $1;
+                       if (!ctx_has_comment($first_line, $linenr)) {
+                               CHK("UNCOMMENTED_DEFINITION",
+                                   "$1 definition without comment\n" . $herecurr);
+                       }
+               }
+# check for memory barriers without a comment.
+
+               my $barriers = qr{
+                       mb|
+                       rmb|
+                       wmb|
+                       read_barrier_depends
+               }x;
+               my $barrier_stems = qr{
+                       mb__before_atomic|
+                       mb__after_atomic|
+                       store_release|
+                       load_acquire|
+                       store_mb|
+                       (?:$barriers)
+               }x;
+               my $all_barriers = qr{
+                       (?:$barriers)|
+                       smp_(?:$barrier_stems)|
+                       virt_(?:$barrier_stems)
+               }x;
+
+               if ($line =~ /\b(?:$all_barriers)\s*\(/) {
+                       if (!ctx_has_comment($first_line, $linenr)) {
+                               WARN("MEMORY_BARRIER",
+                                    "memory barrier without comment\n" . $herecurr);
+                       }
+               }
+
+               my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x;
+
+               if ($realfile !~ m@^include/asm-generic/@ &&
+                   $realfile !~ m@/barrier\.h$@ &&
+                   $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ &&
+                   $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) {
+                       WARN("MEMORY_BARRIER",
+                            "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr);
+               }
+
+# check for waitqueue_active without a comment.
+               if ($line =~ /\bwaitqueue_active\s*\(/) {
+                       if (!ctx_has_comment($first_line, $linenr)) {
+                               WARN("WAITQUEUE_ACTIVE",
+                                    "waitqueue_active without comment\n" . $herecurr);
+                       }
+               }
+
+# check of hardware specific defines
+               if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
+                       CHK("ARCH_DEFINES",
+                           "architecture specific defines should be avoided\n" .  $herecurr);
+               }
+
+# check that the storage class is not after a type
+               if ($line =~ /\b($Type)\s+($Storage)\b/) {
+                       WARN("STORAGE_CLASS",
+                            "storage class '$2' should be located before type '$1'\n" . $herecurr);
+               }
+# Check that the storage class is at the beginning of a declaration
+               if ($line =~ /\b$Storage\b/ &&
+                   $line !~ /^.\s*$Storage/ &&
+                   $line =~ /^.\s*(.+?)\$Storage\s/ &&
+                   $1 !~ /[\,\)]\s*$/) {
+                       WARN("STORAGE_CLASS",
+                            "storage class should be at the beginning of the declaration\n" . $herecurr);
+               }
+
+# check the location of the inline attribute, that it is between
+# storage class and type.
+               if ($line =~ /\b$Type\s+$Inline\b/ ||
+                   $line =~ /\b$Inline\s+$Storage\b/) {
+                       ERROR("INLINE_LOCATION",
+                             "inline keyword should sit between storage class and type\n" . $herecurr);
+               }
+
+# Check for __inline__ and __inline, prefer inline
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $line =~ /\b(__inline__|__inline)\b/) {
+                       if (WARN("INLINE",
+                                "plain inline is preferred over $1\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/;
+
+                       }
+               }
+
+# Check for __attribute__ packed, prefer __packed
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
+                       WARN("PREFER_PACKED",
+                            "__packed is preferred over __attribute__((packed))\n" . $herecurr);
+               }
+
+# Check for __attribute__ aligned, prefer __aligned
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) {
+                       WARN("PREFER_ALIGNED",
+                            "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr);
+               }
+
+# Check for __attribute__ format(printf, prefer __printf
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) {
+                       if (WARN("PREFER_PRINTF",
+                                "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex;
+
+                       }
+               }
+
+# Check for __attribute__ format(scanf, prefer __scanf
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) {
+                       if (WARN("PREFER_SCANF",
+                                "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex;
+                       }
+               }
+
+# Check for __attribute__ weak, or __weak declarations (may have link issues)
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ &&
+                   ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ ||
+                    $line =~ /\b__weak\b/)) {
+                       ERROR("WEAK_DECLARATION",
+                             "Using weak declarations can have unintended link defects\n" . $herecurr);
+               }
+
+# check for c99 types like uint8_t used outside of uapi/ and tools/
+               if ($realfile !~ m@\binclude/uapi/@ &&
+                   $realfile !~ m@\btools/@ &&
+                   $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) {
+                       my $type = $1;
+                       if ($type =~ /\b($typeC99Typedefs)\b/) {
+                               $type = $1;
+                               my $kernel_type = 'u';
+                               $kernel_type = 's' if ($type =~ /^_*[si]/);
+                               $type =~ /(\d+)/;
+                               $kernel_type .= $1;
+                               if (CHK("PREFER_KERNEL_TYPES",
+                                       "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/;
+                               }
+                       }
+               }
+
+# check for cast of C90 native int or longer types constants
+               if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) {
+                       my $cast = $1;
+                       my $const = $2;
+                       if (WARN("TYPECAST_INT_CONSTANT",
+                                "Unnecessary typecast of c90 int constant\n" . $herecurr) &&
+                           $fix) {
+                               my $suffix = "";
+                               my $newconst = $const;
+                               $newconst =~ s/${Int_type}$//;
+                               $suffix .= 'U' if ($cast =~ /\bunsigned\b/);
+                               if ($cast =~ /\blong\s+long\b/) {
+                                       $suffix .= 'LL';
+                               } elsif ($cast =~ /\blong\b/) {
+                                       $suffix .= 'L';
+                               }
+                               $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/;
+                       }
+               }
+
+# check for sizeof(&)
+               if ($line =~ /\bsizeof\s*\(\s*\&/) {
+                       WARN("SIZEOF_ADDRESS",
+                            "sizeof(& should be avoided\n" . $herecurr);
+               }
+
+# check for sizeof without parenthesis
+               if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) {
+                       if (WARN("SIZEOF_PARENTHESIS",
+                                "sizeof $1 should be sizeof($1)\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex;
+                       }
+               }
+
+# check for struct spinlock declarations
+               if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) {
+                       WARN("USE_SPINLOCK_T",
+                            "struct spinlock should be spinlock_t\n" . $herecurr);
+               }
+
+# check for seq_printf uses that could be seq_puts
+               if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) {
+                       my $fmt = get_quoted_string($line, $rawline);
+                       $fmt =~ s/%%//g;
+                       if ($fmt !~ /%/) {
+                               if (WARN("PREFER_SEQ_PUTS",
+                                        "Prefer seq_puts to seq_printf\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/;
+                               }
+                       }
+               }
+
+               # check for vsprintf extension %p<foo> misuses
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
+                   $1 !~ /^_*volatile_*$/) {
+                       my $bad_extension = "";
+                       my $lc = $stat =~ tr@\n@@;
+                       $lc = $lc + $linenr;
+                       for (my $count = $linenr; $count <= $lc; $count++) {
+                               my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
+                               $fmt =~ s/%%//g;
+                               if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNOx]).)/) {
+                                       $bad_extension = $1;
+                                       last;
+                               }
+                       }
+                       if ($bad_extension ne "") {
+                               my $stat_real = raw_line($linenr, 0);
+                               for (my $count = $linenr + 1; $count <= $lc; $count++) {
+                                       $stat_real = $stat_real . "\n" . raw_line($count, 0);
+                               }
+                               WARN("VSPRINTF_POINTER_EXTENSION",
+                                    "Invalid vsprintf pointer extension '$bad_extension'\n" . "$here\n$stat_real\n");
+                       }
+               }
+
+# Check for misused memsets
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) {
+
+                       my $ms_addr = $2;
+                       my $ms_val = $7;
+                       my $ms_size = $12;
+
+                       if ($ms_size =~ /^(0x|)0$/i) {
+                               ERROR("MEMSET",
+                                     "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n");
+                       } elsif ($ms_size =~ /^(0x|)1$/i) {
+                               WARN("MEMSET",
+                                    "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n");
+                       }
+               }
+
+# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar)
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#                      if (WARN("PREFER_ETHER_ADDR_COPY",
+#                               "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
+#                          $fix) {
+#                              $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
+#                      }
+#              }
+
+# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar)
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#                      WARN("PREFER_ETHER_ADDR_EQUAL",
+#                           "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
+#              }
+
+# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr
+# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#
+#                      my $ms_val = $7;
+#
+#                      if ($ms_val =~ /^(?:0x|)0+$/i) {
+#                              if (WARN("PREFER_ETH_ZERO_ADDR",
+#                                       "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
+#                                  $fix) {
+#                                      $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
+#                              }
+#                      } elsif ($ms_val =~ /^(?:0xff|255)$/i) {
+#                              if (WARN("PREFER_ETH_BROADCAST_ADDR",
+#                                       "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
+#                                  $fix) {
+#                                      $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
+#                              }
+#                      }
+#              }
+
+# typecasts on min/max could be min_t/max_t
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) {
+                       if (defined $2 || defined $7) {
+                               my $call = $1;
+                               my $cast1 = deparenthesize($2);
+                               my $arg1 = $3;
+                               my $cast2 = deparenthesize($7);
+                               my $arg2 = $8;
+                               my $cast;
+
+                               if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) {
+                                       $cast = "$cast1 or $cast2";
+                               } elsif ($cast1 ne "") {
+                                       $cast = $cast1;
+                               } else {
+                                       $cast = $cast2;
+                               }
+                               WARN("MINMAX",
+                                    "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n");
+                       }
+               }
+
+# check usleep_range arguments
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) {
+                       my $min = $1;
+                       my $max = $7;
+                       if ($min eq $max) {
+                               WARN("USLEEP_RANGE",
+                                    "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                       } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
+                                $min > $max) {
+                               WARN("USLEEP_RANGE",
+                                    "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n");
+                       }
+               }
+
+# check for naked sscanf
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $line =~ /\bsscanf\b/ &&
+                   ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
+                    $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ &&
+                    $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
+                       my $lc = $stat =~ tr@\n@@;
+                       $lc = $lc + $linenr;
+                       my $stat_real = raw_line($linenr, 0);
+                       for (my $count = $linenr + 1; $count <= $lc; $count++) {
+                               $stat_real = $stat_real . "\n" . raw_line($count, 0);
+                       }
+                       WARN("NAKED_SSCANF",
+                            "unchecked sscanf return value\n" . "$here\n$stat_real\n");
+               }
+
+# check for simple sscanf that should be kstrto<foo>
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $line =~ /\bsscanf\b/) {
+                       my $lc = $stat =~ tr@\n@@;
+                       $lc = $lc + $linenr;
+                       my $stat_real = raw_line($linenr, 0);
+                       for (my $count = $linenr + 1; $count <= $lc; $count++) {
+                               $stat_real = $stat_real . "\n" . raw_line($count, 0);
+                       }
+                       if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) {
+                               my $format = $6;
+                               my $count = $format =~ tr@%@%@;
+                               if ($count == 1 &&
+                                   $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) {
+                                       WARN("SSCANF_TO_KSTRTO",
+                                            "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n");
+                               }
+                       }
+               }
+
+# check for new externs in .h files.
+               if ($realfile =~ /\.h$/ &&
+                   $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) {
+                       if (CHK("AVOID_EXTERNS",
+                               "extern prototypes should be avoided in .h files\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
+                       }
+               }
+
+# check for new externs in .c files.
+               if ($realfile =~ /\.c$/ && defined $stat &&
+                   $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
+               {
+                       my $function_name = $1;
+                       my $paren_space = $2;
+
+                       my $s = $stat;
+                       if (defined $cond) {
+                               substr($s, 0, length($cond), '');
+                       }
+                       if ($s =~ /^\s*;/ &&
+                           $function_name ne 'uninitialized_var')
+                       {
+                               WARN("AVOID_EXTERNS",
+                                    "externs should be avoided in .c files\n" .  $herecurr);
+                       }
+
+                       if ($paren_space =~ /\n/) {
+                               WARN("FUNCTION_ARGUMENTS",
+                                    "arguments for function declarations should follow identifier\n" . $herecurr);
+                       }
+
+               } elsif ($realfile =~ /\.c$/ && defined $stat &&
+                   $stat =~ /^.\s*extern\s+/)
+               {
+                       WARN("AVOID_EXTERNS",
+                            "externs should be avoided in .c files\n" .  $herecurr);
+               }
+
+# check for function declarations that have arguments without identifier names
+               if (defined $stat &&
+                   $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s &&
+                   $1 ne "void") {
+                       my $args = trim($1);
+                       while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
+                               my $arg = trim($1);
+                               if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) {
+                                       WARN("FUNCTION_ARGUMENTS",
+                                            "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
+                               }
+                       }
+               }
+
+# check for function definitions
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) {
+                       $context_function = $1;
+
+# check for multiline function definition with misplaced open brace
+                       my $ok = 0;
+                       my $cnt = statement_rawlines($stat);
+                       my $herectx = $here . "\n";
+                       for (my $n = 0; $n < $cnt; $n++) {
+                               my $rl = raw_line($linenr, $n);
+                               $herectx .=  $rl . "\n";
+                               $ok = 1 if ($rl =~ /^[ \+]\{/);
+                               $ok = 1 if ($rl =~ /\{/ && $n == 0);
+                               last if $rl =~ /^[ \+].*\{/;
+                       }
+                       if (!$ok) {
+                               ERROR("OPEN_BRACE",
+                                     "open brace '{' following function definitions go on the next line\n" . $herectx);
+                       }
+               }
+
+# checks for new __setup's
+               if ($rawline =~ /\b__setup\("([^"]*)"/) {
+                       my $name = $1;
+
+                       if (!grep(/$name/, @setup_docs)) {
+                               CHK("UNDOCUMENTED_SETUP",
+                                   "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr);
+                       }
+               }
+
+# check for pointless casting of kmalloc return
+               if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) {
+                       WARN("UNNECESSARY_CASTS",
+                            "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
+               }
+
+# alloc style
+# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
+                       CHK("ALLOC_SIZEOF_STRUCT",
+                           "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
+               }
+
+# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
+                       my $oldfunc = $3;
+                       my $a1 = $4;
+                       my $a2 = $10;
+                       my $newfunc = "kmalloc_array";
+                       $newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
+                       my $r1 = $a1;
+                       my $r2 = $a2;
+                       if ($a1 =~ /^sizeof\s*\S/) {
+                               $r1 = $a2;
+                               $r2 = $a1;
+                       }
+                       if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ &&
+                           !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) {
+                               my $ctx = '';
+                               my $herectx = $here . "\n";
+                               my $cnt = statement_rawlines($stat);
+                               for (my $n = 0; $n < $cnt; $n++) {
+                                       $herectx .= raw_line($linenr, $n) . "\n";
+                               }
+                               if (WARN("ALLOC_WITH_MULTIPLY",
+                                        "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
+                                   $cnt == 1 &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
+                               }
+                       }
+               }
+
+# check for krealloc arg reuse
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) {
+                       WARN("KREALLOC_ARG_REUSE",
+                            "Reusing the krealloc arg is almost always a bug\n" . $herecurr);
+               }
+
+# check for alloc argument mismatch
+               if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) {
+                       WARN("ALLOC_ARRAY_ARGS",
+                            "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
+               }
+
+# check for multiple semicolons
+               if ($line =~ /;\s*;\s*$/) {
+                       if (WARN("ONE_SEMICOLON",
+                                "Statements terminations use 1 semicolon\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g;
+                       }
+               }
+
+# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
+               if ($realfile !~ m@^include/uapi/@ &&
+                   $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
+                       my $ull = "";
+                       $ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
+                       if (CHK("BIT_MACRO",
+                               "Prefer using the BIT$ull macro\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/;
+                       }
+               }
+
+# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
+               if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+                       my $config = $1;
+                       if (WARN("PREFER_IS_ENABLED",
+                                "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
+                       }
+               }
+
+# check for case / default statements not preceded by break/fallthrough/switch
+               if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
+                       my $has_break = 0;
+                       my $has_statement = 0;
+                       my $count = 0;
+                       my $prevline = $linenr;
+                       while ($prevline > 1 && ($file || $count < 3) && !$has_break) {
+                               $prevline--;
+                               my $rline = $rawlines[$prevline - 1];
+                               my $fline = $lines[$prevline - 1];
+                               last if ($fline =~ /^\@\@/);
+                               next if ($fline =~ /^\-/);
+                               next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
+                               $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
+                               next if ($fline =~ /^.[\s$;]*$/);
+                               $has_statement = 1;
+                               $count++;
+                               $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/);
+                       }
+                       if (!$has_break && $has_statement) {
+                               WARN("MISSING_BREAK",
+                                    "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr);
+                       }
+               }
+
+# check for switch/default statements without a break;
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
+                       my $ctx = '';
+                       my $herectx = $here . "\n";
+                       my $cnt = statement_rawlines($stat);
+                       for (my $n = 0; $n < $cnt; $n++) {
+                               $herectx .= raw_line($linenr, $n) . "\n";
+                       }
+                       WARN("DEFAULT_NO_BREAK",
+                            "switch default: should use break\n" . $herectx);
+               }
+
+# check for gcc specific __FUNCTION__
+               if ($line =~ /\b__FUNCTION__\b/) {
+                       if (WARN("USE_FUNC",
+                                "__func__ should be used instead of gcc specific __FUNCTION__\n"  . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g;
+                       }
+               }
+
+# check for uses of __DATE__, __TIME__, __TIMESTAMP__
+               while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) {
+                       ERROR("DATE_TIME",
+                             "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr);
+               }
+
+# check for use of yield()
+               if ($line =~ /\byield\s*\(\s*\)/) {
+                       WARN("YIELD",
+                            "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n"  . $herecurr);
+               }
+
+# check for comparisons against true and false
+               if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) {
+                       my $lead = $1;
+                       my $arg = $2;
+                       my $test = $3;
+                       my $otype = $4;
+                       my $trail = $5;
+                       my $op = "!";
+
+                       ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i);
+
+                       my $type = lc($otype);
+                       if ($type =~ /^(?:true|false)$/) {
+                               if (("$test" eq "==" && "$type" eq "true") ||
+                                   ("$test" eq "!=" && "$type" eq "false")) {
+                                       $op = "";
+                               }
+
+                               CHK("BOOL_COMPARISON",
+                                   "Using comparison to $otype is error prone\n" . $herecurr);
+
+## maybe suggesting a correct construct would better
+##                                 "Using comparison to $otype is error prone.  Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr);
+
+                       }
+               }
+
+# check for semaphores initialized locked
+               if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) {
+                       WARN("CONSIDER_COMPLETION",
+                            "consider using a completion\n" . $herecurr);
+               }
+
+# recommend kstrto* over simple_strto* and strict_strto*
+               if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
+                       WARN("CONSIDER_KSTRTO",
+                            "$1 is obsolete, use k$3 instead\n" . $herecurr);
+               }
+
+# check for __initcall(), use device_initcall() explicitly or more appropriate function please
+               if ($line =~ /^.\s*__initcall\s*\(/) {
+                       WARN("USE_DEVICE_INITCALL",
+                            "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
+               }
+
+# check for various structs that are normally const (ops, kgdb, device_tree)
+# and avoid what seem like struct definitions 'struct foo {'
+               if ($line !~ /\bconst\b/ &&
+                   $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
+                       WARN("CONST_STRUCT",
+                            "struct $1 should normally be const\n" . $herecurr);
+               }
+
+# use of NR_CPUS is usually wrong
+# ignore definitions of NR_CPUS and usage to define arrays as likely right
+               if ($line =~ /\bNR_CPUS\b/ &&
+                   $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
+                   $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
+                   $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
+                   $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
+                   $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
+               {
+                       WARN("NR_CPUS",
+                            "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
+               }
+
+# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong.
+               if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) {
+                       ERROR("DEFINE_ARCH_HAS",
+                             "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr);
+               }
+
+# likely/unlikely comparisons similar to "(likely(foo) > 0)"
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) {
+                       WARN("LIKELY_MISUSE",
+                            "Using $1 should generally have parentheses around the comparison\n" . $herecurr);
+               }
+
+# whine mightly about in_atomic
+               if ($line =~ /\bin_atomic\s*\(/) {
+                       if ($realfile =~ m@^drivers/@) {
+                               ERROR("IN_ATOMIC",
+                                     "do not use in_atomic in drivers\n" . $herecurr);
+                       } elsif ($realfile !~ m@^kernel/@) {
+                               WARN("IN_ATOMIC",
+                                    "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
+                       }
+               }
+
+# whine about ACCESS_ONCE
+               if ($^V && $^V ge 5.10.0 &&
+                   $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) {
+                       my $par = $1;
+                       my $eq = $2;
+                       my $fun = $3;
+                       $par =~ s/^\(\s*(.*)\s*\)$/$1/;
+                       if (defined($eq)) {
+                               if (WARN("PREFER_WRITE_ONCE",
+                                        "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/;
+                               }
+                       } else {
+                               if (WARN("PREFER_READ_ONCE",
+                                        "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) &&
+                                   $fix) {
+                                       $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/;
+                               }
+                       }
+               }
+
+# check for mutex_trylock_recursive usage
+               if ($line =~ /mutex_trylock_recursive/) {
+                       ERROR("LOCKING",
+                             "recursive locking is bad, do not use this ever.\n" . $herecurr);
+               }
+
+# check for lockdep_set_novalidate_class
+               if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
+                   $line =~ /__lockdep_no_validate__\s*\)/ ) {
+                       if ($realfile !~ m@^kernel/lockdep@ &&
+                           $realfile !~ m@^include/linux/lockdep@ &&
+                           $realfile !~ m@^drivers/base/core@) {
+                               ERROR("LOCKDEP",
+                                     "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
+                       }
+               }
+
+               if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ ||
+                   $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) {
+                       WARN("EXPORTED_WORLD_WRITABLE",
+                            "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+               }
+
+# Mode permission misuses where it seems decimal should be octal
+# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
+               if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
+                   $line =~ /$mode_perms_search/) {
+                       foreach my $entry (@mode_permission_funcs) {
+                               my $func = $entry->[0];
+                               my $arg_pos = $entry->[1];
+
+                               my $lc = $stat =~ tr@\n@@;
+                               $lc = $lc + $linenr;
+                               my $stat_real = raw_line($linenr, 0);
+                               for (my $count = $linenr + 1; $count <= $lc; $count++) {
+                                       $stat_real = $stat_real . "\n" . raw_line($count, 0);
+                               }
+
+                               my $skip_args = "";
+                               if ($arg_pos > 1) {
+                                       $arg_pos--;
+                                       $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
+                               }
+                               my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
+                               if ($stat =~ /$test/) {
+                                       my $val = $1;
+                                       $val = $6 if ($skip_args ne "");
+                                       if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
+                                           ($val =~ /^$Octal$/ && length($val) ne 4)) {
+                                               ERROR("NON_OCTAL_PERMISSIONS",
+                                                     "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
+                                       }
+                                       if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
+                                               ERROR("EXPORTED_WORLD_WRITABLE",
+                                                     "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
+                                       }
+                               }
+                       }
+               }
+
+# check for uses of S_<PERMS> that could be octal for readability
+               if ($line =~ /\b$mode_perms_string_search\b/) {
+                       my $val = "";
+                       my $oval = "";
+                       my $to = 0;
+                       my $curpos = 0;
+                       my $lastpos = 0;
+                       while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
+                               $curpos = pos($line);
+                               my $match = $2;
+                               my $omatch = $1;
+                               last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
+                               $lastpos = $curpos;
+                               $to |= $mode_permission_string_types{$match};
+                               $val .= '\s*\|\s*' if ($val ne "");
+                               $val .= $match;
+                               $oval .= $omatch;
+                       }
+                       $oval =~ s/^\s*\|\s*//;
+                       $oval =~ s/\s*\|\s*$//;
+                       my $octal = sprintf("%04o", $to);
+                       if (WARN("SYMBOLIC_PERMS",
+                                "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/$val/$octal/;
+                       }
+               }
+
+# validate content of MODULE_LICENSE against list from include/linux/module.h
+               if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
+                       my $extracted_string = get_quoted_string($line, $rawline);
+                       my $valid_licenses = qr{
+                                               GPL|
+                                               GPL\ v2|
+                                               GPL\ and\ additional\ rights|
+                                               Dual\ BSD/GPL|
+                                               Dual\ MIT/GPL|
+                                               Dual\ MPL/GPL|
+                                               Proprietary
+                                       }x;
+                       if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) {
+                               WARN("MODULE_LICENSE",
+                                    "unknown module license " . $extracted_string . "\n" . $herecurr);
+                       }
+               }
+       }
+
+       # If we have no input at all, then there is nothing to report on
+       # so just keep quiet.
+       if ($#rawlines == -1) {
+               exit(0);
+       }
+
+       # In mailback mode only produce a report in the negative, for
+       # things that appear to be patches.
+       if ($mailback && ($clean == 1 || !$is_patch)) {
+               exit(0);
+       }
+
+       # This is not a patch, and we are are in 'no-patch' mode so
+       # just keep quiet.
+       if (!$chk_patch && !$is_patch) {
+               exit(0);
+       }
+
+       if (!$is_patch && $filename !~ /cover-letter\.patch$/) {
+               ERROR("NOT_UNIFIED_DIFF",
+                     "Does not appear to be a unified-diff format patch\n");
+       }
+       if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) {
+               ERROR("MISSING_SIGN_OFF",
+                     "Missing Signed-off-by: line(s)\n");
+       }
+
+       print report_dump();
+       if ($summary && !($clean == 1 && $quiet == 1)) {
+               print "$filename " if ($summary_file);
+               print "total: $cnt_error errors, $cnt_warn warnings, " .
+                       (($check)? "$cnt_chk checks, " : "") .
+                       "$cnt_lines lines checked\n";
+       }
+
+       if ($quiet == 0) {
+               # If there were any defects found and not already fixing them
+               if (!$clean and !$fix) {
+                       print << "EOM"
+
+NOTE: For some of the reported defects, checkpatch may be able to
+      mechanically convert to the typical style using --fix or --fix-inplace.
+EOM
+               }
+               # If there were whitespace errors which cleanpatch can fix
+               # then suggest that.
+               if ($rpt_cleaners) {
+                       $rpt_cleaners = 0;
+                       print << "EOM"
+
+NOTE: Whitespace errors detected.
+      You may wish to use scripts/cleanpatch or scripts/cleanfile
+EOM
+               }
+       }
+
+       if ($clean == 0 && $fix &&
+           ("@rawlines" ne "@fixed" ||
+            $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) {
+               my $newfile = $filename;
+               $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace);
+               my $linecount = 0;
+               my $f;
+
+               @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted);
+
+               open($f, '>', $newfile)
+                   or die "$P: Can't open $newfile for write\n";
+               foreach my $fixed_line (@fixed) {
+                       $linecount++;
+                       if ($file) {
+                               if ($linecount > 3) {
+                                       $fixed_line =~ s/^\+//;
+                                       print $f $fixed_line . "\n";
+                               }
+                       } else {
+                               print $f $fixed_line . "\n";
+                       }
+               }
+               close($f);
+
+               if (!$quiet) {
+                       print << "EOM";
+
+Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
+
+Do _NOT_ trust the results written to this file.
+Do _NOT_ submit these changes without inspecting them for correctness.
+
+This EXPERIMENTAL file is simply a convenience to help rewrite patches.
+No warranties, expressed or implied...
+EOM
+               }
+       }
+
+       if ($quiet == 0) {
+               print "\n";
+               if ($clean == 1) {
+                       print "$vname has no obvious style problems and is ready for submission.\n";
+               } else {
+                       print "$vname has style problems, please review.\n";
+               }
+       }
+       return $clean;
+}
diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh
new file mode 100755 (executable)
index 0000000..3536df7
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Check a patch for style errors.
+usage="./checkpatch.sh <patch> <tree>"
+patch=$1
+tree=$2
+checkpatch="$tree/tools/checkpatch.pl --no-tree -f"
+ignore="ldpd\|babeld"
+cwd=${PWD##*/}
+dirty=0
+stat=0
+
+if [[ -z "$1" || -z "$2" ]]; then
+  echo "$usage"
+  exit 0
+fi
+
+# remove temp directories
+rm -rf /tmp/f1 /tmp/f2
+
+# save working tree
+if git -C $tree status --porcelain | egrep --silent '^(\?\?|.[DM])'; then
+  echo "Detected dirty tree, caching state..."
+  dirty=1
+  git -C $tree config gc.auto 0;
+  td=$(git -C $tree status -z | grep -z "^[ARM]D" | cut -z -d' ' -f2- | tr '\0' '\n')
+  INDEX=$(git -C $tree write-tree)
+  git -C $tree add -f .
+  WORKTREE=$(git -C $tree write-tree)
+  echo "Saved index to $INDEX"
+  echo "Saved working tree to $WORKTREE"
+fi
+
+# double check
+if git -C $tree status --porcelain | egrep --silent '^(\?\?|.[DM])'; then
+  echo "[!] git working directory must be clean."
+  exit 1
+fi
+
+git -C $tree reset --hard
+git -C $tree apply < $patch
+mkdir -p /tmp/f1 /tmp/f2
+mod=$(git -C $tree ls-files -m | grep ".*\.[ch]" | grep -v $ignore)
+mod+=" $(git -C $tree ls-files --others --exclude-standard | grep '.*\.[ch]' | grep -v $ignore)"
+echo $mod
+if [ -z "$mod" ]; then
+  echo "There doesn't seem to be any changes."
+else
+  cp $tree/$mod /tmp/f1/
+  git -C $tree reset --hard
+  git -C $tree clean -fd
+  cp $tree/$mod /tmp/f2/
+  echo "Running style checks..."
+  for file in /tmp/f1/*; do
+    echo "$checkpatch $file > $file _cp"
+    $checkpatch $file > "$file"_cp 2> /dev/null
+  done
+  for file in /tmp/f2/*; do
+    echo "$checkpatch $file > $file _cp"
+    $checkpatch $file > "$file"_cp 2> /dev/null
+  done
+  echo "Done."
+  for file in /tmp/f1/*_cp; do
+    echo "Report for $(basename $file _cp)"
+    echo "==============================================="
+    if [ -a /tmp/f2/$(basename $file) ]; then
+      diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+    else
+      cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+    fi
+    if [ "$?" -eq "0" ]; then
+      stat=1
+    fi
+  done
+fi
+
+# restore working tree
+if [ $dirty -eq 1 ]; then
+  git -C $tree read-tree $WORKTREE;
+  git -C $tree checkout-index -af;
+  git -C $tree read-tree $INDEX;
+  if [ -n "$td" ]; then
+    rm $td
+  fi
+  git -C $tree config --unset gc.auto;
+fi
+
+exit $stat
index 8dc16f420922be9ebca533e846393a1720350423..6bf55b77405661decbe488af5c3006f4ac705b1a 100644 (file)
@@ -396,7 +396,7 @@ static void parse_schedule_item(const char *string, struct schedule_item *item)
 
        if (!strcmp(string, "forever")) {
                item->type = sched_forever;
-       } else if (isdigit(string[0])) {
+       } else if (isdigit((int)string[0])) {
                item->type = sched_timeout;
                if (parse_integer(string, &item->value) != 0)
                        badusage("invalid timeout value in schedule");
index 3ddb6aba54092765a4caea729beed5ad00d83047..c9b6f501606416c9c1207e6c8ef8ee190e45c9bc 100644 (file)
@@ -75,6 +75,7 @@ vtysh_scan += $(top_srcdir)/ospfd/ospf_opaque.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_ri.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_routemap.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_te.c
+vtysh_scan += $(top_srcdir)/ospfd/ospf_sr.c
 vtysh_scan += $(top_srcdir)/ospfd/ospf_vty.c
 endif
 
index 097f39fcf0370ae7458f8721736422ade92ba56f..94c4ba43305be2e2e461d978477913bead00ffd2 100644 (file)
@@ -504,11 +504,11 @@ static char *trim(char *s)
                return s;
 
        end = s + size - 1;
-       while (end >= s && isspace(*end))
+       while (end >= s && isspace((int)*end))
                end--;
        *(end + 1) = '\0';
 
-       while (*s && isspace(*s))
+       while (*s && isspace((int)*s))
                s++;
 
        return s;
index ba028ed09cf64f47cd1112506583faddc893cff5..2a3b95058ef2bb1cceb8ade6b93554f0f37439d6 100644 (file)
@@ -40,6 +40,7 @@
 #include "privs.h"
 #include "vrf.h"
 
+#include "zebra/rt.h"
 #include "zebra/interface.h"
 #include "zebra/zserv.h"
 #include "zebra/debug.h"
index bb4ff5bee809ed8692c942d2fcd3c596c998ab4c..54d45b889a303a9f4c9b014340bb7257b8c869a1 100644 (file)
@@ -60,15 +60,17 @@ enum southbound_results {
  * semantics so we will end up with a delete than
  * a re-add.
  */
-extern void kernel_route_rib(struct prefix *p, struct prefix *src_p,
-                            struct route_entry *old, struct route_entry *new);
+extern void kernel_route_rib(struct route_node *rn, struct prefix *p,
+                            struct prefix *src_p, struct route_entry *old,
+                            struct route_entry *new);
 
 /*
  * So route install/failure may not be immediately known
  * so let's separate it out and allow the result to
  * be passed back up.
  */
-extern void kernel_route_rib_pass_fail(struct prefix *p,
+extern void kernel_route_rib_pass_fail(struct route_node *rn,
+                                      struct prefix *p,
                                       struct route_entry *re,
                                       enum southbound_results res);
 
index 0221162a3091617cd9080bf7d954da5853ea213e..20cc292e111cffc35a9e979bfe4cffce4bade81a 100644 (file)
@@ -1642,8 +1642,9 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
        return suc;
 }
 
-void kernel_route_rib(struct prefix *p, struct prefix *src_p,
-                     struct route_entry *old, struct route_entry *new)
+void kernel_route_rib(struct route_node *rn, struct prefix *p,
+                     struct prefix *src_p, struct route_entry *old,
+                     struct route_entry *new)
 {
        int ret = 0;
 
@@ -1672,7 +1673,7 @@ void kernel_route_rib(struct prefix *p, struct prefix *src_p,
                        ret = netlink_route_multipath(RTM_NEWROUTE, p,
                                                      src_p, new, 0);
                }
-               kernel_route_rib_pass_fail(p, new,
+               kernel_route_rib_pass_fail(rn, p, new,
                                           (!ret) ?
                                           SOUTHBOUND_INSTALL_SUCCESS :
                                           SOUTHBOUND_INSTALL_FAILURE);
@@ -1682,7 +1683,7 @@ void kernel_route_rib(struct prefix *p, struct prefix *src_p,
        if (old) {
                ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0);
 
-               kernel_route_rib_pass_fail(p, old,
+               kernel_route_rib_pass_fail(rn, p, old,
                                           (!ret) ?
                                           SOUTHBOUND_DELETE_SUCCESS :
                                           SOUTHBOUND_DELETE_FAILURE);
index 09fdf0b2d38f64abeb65646c3a3f46939246c851..6d4af1203c7d457e0174d31bee188ccd7b1bf45b 100644 (file)
@@ -387,8 +387,9 @@ static int kernel_rtm(int cmd, struct prefix *p, struct route_entry *re)
        return 0;
 }
 
-void kernel_route_rib(struct prefix *p, struct prefix *src_p,
-                     struct route_entry *old, struct route_entry *new)
+void kernel_route_rib(struct route_node *rn, struct prefix *p,
+                     struct prefix *src_p, struct route_entry *old,
+                     struct route_entry *new)
 {
        int route = 0;
 
@@ -410,12 +411,12 @@ void kernel_route_rib(struct prefix *p, struct prefix *src_p,
                zlog_err("Can't lower privileges");
 
        if (new) {
-               kernel_route_rib_pass_fail(p, new,
+               kernel_route_rib_pass_fail(rn, p, new,
                                           (!route) ?
                                           SOUTHBOUND_INSTALL_SUCCESS :
                                           SOUTHBOUND_INSTALL_FAILURE);
        } else {
-               kernel_route_rib_pass_fail(p, old,
+               kernel_route_rib_pass_fail(rn, p, old,
                                           (!route) ?
                                           SOUTHBOUND_DELETE_SUCCESS :
                                           SOUTHBOUND_DELETE_FAILURE);
index 900474a6111fdf85b8dbfcb1b9ae510293631c1e..27a49716913b41a0d4b95fce0260a88a27f10748 100644 (file)
@@ -316,6 +316,12 @@ void mpls_ldp_lsp_uninstall_all(struct hash_backet *backet, void *ctxt);
  */
 void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi);
 
+/*
+ * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry.
+ * If no other NHLFEs exist, the entry would be deleted.
+ */
+void mpls_sr_lsp_uninstall_all(struct hash_backet *backet, void *ctxt);
+
 #if defined(HAVE_CUMULUS)
 /*
  * Check that the label values used in LSP creation are consistent. The
@@ -455,6 +461,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type)
                return ZEBRA_ROUTE_LDP;
        case ZEBRA_LSP_BGP:
                return ZEBRA_ROUTE_BGP;
+       case ZEBRA_LSP_SR:
+               return ZEBRA_ROUTE_OSPF;
        case ZEBRA_LSP_NONE:
        default:
                return ZEBRA_ROUTE_KERNEL;
@@ -471,6 +479,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type)
                return "LDP";
        case ZEBRA_LSP_BGP:
                return "BGP";
+       case ZEBRA_LSP_SR:
+               return "SR";
        default:
                return "Unknown";
        }
index 953f74ecec177f711e32acce0ca307a7a90cc7e4..7f5fd472f1f96d5ae839ca3bf25c659a36143cbe 100644 (file)
@@ -816,6 +816,8 @@ int zebra_ptm_bfd_dst_register(struct zserv *client, u_short length,
                           ptm_cb.out_data);
        zebra_ptm_send_message(ptm_cb.out_data, data_len);
 
+       return 0;
+
 stream_failure:
        ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
        return 0;
@@ -946,6 +948,8 @@ int zebra_ptm_bfd_dst_deregister(struct zserv *client, u_short length,
 
        zebra_ptm_send_message(ptm_cb.out_data, data_len);
 
+       return 0;
+
 stream_failure:
        ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
        return 0;
@@ -957,7 +961,7 @@ int zebra_ptm_bfd_client_register(struct zserv *client,
 {
        struct stream *s;
        unsigned int pid;
-       void *out_ctxt;
+       void *out_ctxt = NULL;
        char tmp_buf[64];
        int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
 
@@ -999,7 +1003,12 @@ int zebra_ptm_bfd_client_register(struct zserv *client,
 
        SET_FLAG(ptm_cb.client_flags[client->proto],
                 ZEBRA_PTM_BFD_CLIENT_FLAG_REG);
+
+       return 0;
+
 stream_failure:
+       if (out_ctxt)
+               ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
        return 0;
 }
 
index 78077a4b3d3f93116499f4c34e44424c6294d668..b7b4a159dac58af7fac02465fabf15b7bf0b8fc0 100644 (file)
@@ -998,14 +998,19 @@ int zebra_rib_labeled_unicast(struct route_entry *re)
        return 1;
 }
 
-void kernel_route_rib_pass_fail(struct prefix *p, struct route_entry *re,
+void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
+                               struct route_entry *re,
                                enum southbound_results res)
 {
        struct nexthop *nexthop;
        char buf[PREFIX_STRLEN];
+       rib_dest_t *dest;
+
+       dest = rib_dest_from_rnode(rn);
 
        switch (res) {
        case SOUTHBOUND_INSTALL_SUCCESS:
+               dest->selected_fib = re;
                for (ALL_NEXTHOPS(re->nexthop, nexthop)) {
                        if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
                                continue;
@@ -1019,16 +1024,37 @@ void kernel_route_rib_pass_fail(struct prefix *p, struct route_entry *re,
                                         p, ZAPI_ROUTE_INSTALLED);
                break;
        case SOUTHBOUND_INSTALL_FAILURE:
+               /*
+                * I am not sure this is the right thing to do here
+                * but the code always set selected_fib before
+                * this assignment was moved here.
+                */
+               dest->selected_fib = re;
+
                zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
                                         p, ZAPI_ROUTE_FAIL_INSTALL);
                zlog_warn("%u:%s: Route install failed", re->vrf_id,
                          prefix2str(p, buf, sizeof(buf)));
                break;
        case SOUTHBOUND_DELETE_SUCCESS:
+               /*
+                * The case where selected_fib is not re is
+                * when we have received a system route
+                * that is overriding our installed route
+                * as such we should leave the selected_fib
+                * pointer alone
+                */
+               if (dest->selected_fib == re)
+                       dest->selected_fib = NULL;
                for (ALL_NEXTHOPS(re->nexthop, nexthop))
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
                break;
        case SOUTHBOUND_DELETE_FAILURE:
+               /*
+                * Should we set this to NULL if the
+                * delete fails?
+                */
+               dest->selected_fib = NULL;
                zlog_warn("%u:%s: Route Deletion failure", re->vrf_id,
                          prefix2str(p, buf, sizeof(buf)));
                break;
@@ -1083,7 +1109,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
         * the kernel.
         */
        hook_call(rib_update, rn, "installing in kernel");
-       kernel_route_rib(p, src_p, old, re);
+       kernel_route_rib(rn, p, src_p, old, re);
        zvrf->installs++;
 
        return;
@@ -1110,7 +1136,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
         * the kernel.
         */
        hook_call(rib_update, rn, "uninstalling from kernel");
-       kernel_route_rib(p, src_p, re, NULL);
+       kernel_route_rib(rn, p, src_p, re, NULL);
        zvrf->removals++;
 
        return;
@@ -1132,8 +1158,6 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
                /* If labeled-unicast route, uninstall transit LSP. */
                if (zebra_rib_labeled_unicast(re))
                        zebra_mpls_lsp_uninstall(info->zvrf, rn, re);
-
-               dest->selected_fib = NULL;
        }
 
        if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) {
@@ -1218,7 +1242,6 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
                return;
        }
 
-       dest->selected_fib = new;
        if (IS_ZEBRA_DEBUG_RIB) {
                char buf[SRCDEST2STR_BUFFER];
                srcdest_rnode2str(rn, buf, sizeof(buf));
@@ -1232,6 +1255,8 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
 
        if (!RIB_SYSTEM_ROUTE(new))
                rib_install_kernel(rn, new, NULL);
+       else
+               dest->selected_fib = new;
 
        UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
 }
@@ -1256,8 +1281,17 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn,
 
        if (!RIB_SYSTEM_ROUTE(old))
                rib_uninstall_kernel(rn, old);
-
-       dest->selected_fib = NULL;
+       else {
+               /*
+                * We are setting this to NULL here
+                * because that is what we traditionally
+                * have been doing.  I am not positive
+                * that this is the right thing to do
+                * but let's leave the code alone
+                * for the RIB_SYSTEM_ROUTE case
+                */
+               dest->selected_fib = NULL;
+       }
 
        /* Update nexthop for route, reset changed flag. */
        nexthop_active_update(rn, old, 1);
@@ -1271,7 +1305,6 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
 {
        struct nexthop *nexthop = NULL;
        int nh_active = 0;
-       int installed = 1;
        rib_dest_t *dest = rib_dest_from_rnode(rn);
 
        /*
@@ -1321,11 +1354,23 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
                                        zebra_mpls_lsp_install(zvrf, rn, new);
 
                                rib_install_kernel(rn, new, old);
+                       } else {
+                               /*
+                                * We do not need to install the
+                                * selected route because it
+                                * is already isntalled by
+                                * the system( ie not us )
+                                * so just mark it as winning
+                                * we do need to ensure that
+                                * if we uninstall a route
+                                * from ourselves we don't
+                                * over write this pointer
+                                */
+                               dest->selected_fib = NULL;
                        }
-
                        /* If install succeeded or system route, cleanup flags
                         * for prior route. */
-                       if (installed && new != old) {
+                       if (new != old) {
                                if (RIB_SYSTEM_ROUTE(new)) {
                                        if (!RIB_SYSTEM_ROUTE(old))
                                                rib_uninstall_kernel(rn, old);
@@ -1336,10 +1381,6 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
                                                           NEXTHOP_FLAG_FIB);
                                }
                        }
-
-                       /* Update for redistribution. */
-                       if (installed)
-                               dest->selected_fib = new;
                }
 
                /*
@@ -1347,7 +1388,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
                 * failed, we
                 * may need to uninstall and delete for redistribution.
                 */
-               if (!nh_active || !installed) {
+               if (!nh_active) {
                        if (IS_ZEBRA_DEBUG_RIB) {
                                char buf[SRCDEST2STR_BUFFER];
                                srcdest_rnode2str(rn, buf, sizeof(buf));
@@ -1374,7 +1415,8 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
 
                        if (!RIB_SYSTEM_ROUTE(old))
                                rib_uninstall_kernel(rn, old);
-                       dest->selected_fib = NULL;
+                       else
+                               dest->selected_fib = NULL;
                }
        } else {
                /*
@@ -1387,12 +1429,12 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
                 * to add routes.
                 */
                if (!RIB_SYSTEM_ROUTE(new)) {
-                       int in_fib = 0;
+                       bool in_fib = false;
 
                        for (ALL_NEXTHOPS(new->nexthop, nexthop))
                                if (CHECK_FLAG(nexthop->flags,
                                               NEXTHOP_FLAG_FIB)) {
-                                       in_fib = 1;
+                                       in_fib = true;
                                        break;
                                }
                        if (!in_fib)
@@ -2056,6 +2098,9 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
                dest->routes = re->next;
        }
 
+       if (dest->selected_fib == re)
+               dest->selected_fib = NULL;
+
        /* free RE and nexthops */
        zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn);
        nexthops_free(re->nexthop);
@@ -2439,6 +2484,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                                        UNSET_FLAG(rtnh->flags,
                                                   NEXTHOP_FLAG_FIB);
 
+                               /*
+                                * This is a non FRR route
+                                * as such we should mark
+                                * it as deleted
+                                */
                                dest->selected_fib = NULL;
                        } else {
                                /* This means someone else, other than Zebra,
index 8983bdee09e2ab22a75ed325fc316559ba334d4c..cea46ffc181cba294345cdd67e9e44d255c2cbc4 100644 (file)
@@ -1034,6 +1034,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                                stream_putc(s, nexthop->type);
                                switch (nexthop->type) {
                                case NEXTHOP_TYPE_IPV4:
+                               case NEXTHOP_TYPE_IPV4_IFINDEX:
                                        stream_put_in_addr(s,
                                                           &nexthop->gate.ipv4);
                                        stream_putl(s, nexthop->ifindex);
@@ -1041,15 +1042,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
                                case NEXTHOP_TYPE_IFINDEX:
                                        stream_putl(s, nexthop->ifindex);
                                        break;
-                               case NEXTHOP_TYPE_IPV4_IFINDEX:
-                                       stream_put_in_addr(s,
-                                                          &nexthop->gate.ipv4);
-                                       stream_putl(s, nexthop->ifindex);
-                                       break;
                                case NEXTHOP_TYPE_IPV6:
-                                       stream_put(s, &nexthop->gate.ipv6, 16);
-                                       stream_putl(s, nexthop->ifindex);
-                                       break;
                                case NEXTHOP_TYPE_IPV6_IFINDEX:
                                        stream_put(s, &nexthop->gate.ipv6, 16);
                                        stream_putl(s, nexthop->ifindex);
index 89cb2fc4881efb5dae49482f55f44947c0c605bf..4c619e5782743a227121d8b45e9f6a06b809d9c9 100644 (file)
@@ -1193,7 +1193,7 @@ static void *route_set_src_compile(const char *arg)
        union g_addr src, *psrc;
 
        if ((inet_pton(AF_INET6, arg, &src.ipv6) == 1)
-           || (src.ipv4.s_addr && (inet_pton(AF_INET, arg, &src.ipv4) == 1))) {
+           || (inet_pton(AF_INET, arg, &src.ipv4) == 1)) {
                psrc = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(union g_addr));
                *psrc = src;
                return psrc;
index 4310bd394d3f0359d837efa3b854992aa66ed1f9..b9b3048486f7132b94d5dade28790c2a3624c77c 100644 (file)
@@ -568,6 +568,7 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id,
                        info->afi = afi;
                        info->safi = SAFI_UNICAST;
                        table->info = info;
+                       table->cleanup = zebra_rtable_node_cleanup;
                        zvrf->other_table[afi][table_id] = table;
                }
 
index 89ae1e81c39169c15127b01e020dd2ce2a2e716e..f8c9833854926a7152bd97395a34f6511e9bc989 100644 (file)
@@ -501,17 +501,21 @@ DEFPY(ip_route_address_interface,
                ifname = NULL;
        }
 
-       nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
-       if (!nh_zvrf) {
-               vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       nexthop_vrf);
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n",
+                       vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
        if (!nh_zvrf) {
                vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       vrf);
+                       nexthop_vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
@@ -605,20 +609,25 @@ DEFPY(ip_route,
                ifname = NULL;
        }
 
-       nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
-       if (!nh_zvrf) {
-               vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       nexthop_vrf);
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n",
+                       vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
        if (!nh_zvrf) {
                vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       vrf);
+                       nexthop_vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
+
        return zebra_static_route_leak(vty, zvrf, nh_zvrf,
                                       AFI_IP, SAFI_UNICAST, no, prefix,
                                       mask_str, NULL, gate_str, ifname, flag,
@@ -2030,17 +2039,21 @@ DEFPY(ipv6_route_address_interface,
        struct zebra_vrf *zvrf;
        struct zebra_vrf *nh_zvrf;
 
-       nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
-       if (!nh_zvrf) {
-               vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       nexthop_vrf);
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n",
+                       vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
        if (!nh_zvrf) {
                vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       vrf);
+                       nexthop_vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
@@ -2121,17 +2134,21 @@ DEFPY(ipv6_route,
        struct zebra_vrf *zvrf;
        struct zebra_vrf *nh_zvrf;
 
-       nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
-       if (!nh_zvrf) {
-               vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       nexthop_vrf);
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n",
+                       vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }
 
-       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
        if (!nh_zvrf) {
                vty_out(vty, "%% nexthop vrf %s is not defined\n",
-                       vrf);
+                       nexthop_vrf);
                return CMD_WARNING_CONFIG_FAILED;
        }