Showing posts with label OSPF. Show all posts
Showing posts with label OSPF. Show all posts

Reverse lookup of OSPF router IDs

If you store the reverse mapping for the routers’ loopback interfaces in DNS or configure the name-to-address mappings with the ip host commands, you can use the ip ospf name-lookup global configuration command to display the OSPF router IDs as router names.

For example, I’ve configured names of all provider routers’ loopback interfaces in one of my MPLS VPN labs:

P#show run | inc host
ip host PE-A 10.0.1.1
ip host PE-B 10.0.1.2
ip host PE-C 10.0.1.5
ip host P 10.0.1.6

After that, I’ve configured the ip ospf name-lookup command and the OSPF show commands started printing names instead of IP addresses:

P#show ip ospf database

            OSPF Router with ID (10.0.1.6) (Process ID 1)

                Router Link States (Area 0)

Link ID    ADV Router   Age   Seq#       Checksum Link count
10.0.1.1   PE-A         56    0x80000002 0x000C1F 3
10.0.1.2   PE-B         57    0x80000002 0x003FD8 3
10.0.1.5   PE-C         59    0x80000002 0x0027CF 3
10.0.1.6   P            53    0x80000003 0x00B675 7

Unfortunately, this functionality does not implement its full potential. For example, when examining router link states, the router ID is displayed as name, but the adjacent router IDs are not. Too bad, you still have to know the router IDs by heart .

P#show ip ospf database router 10.0.1.6

            OSPF Router with ID (10.0.1.6) (Process ID 1)

                Router Link States (Area 0)

  LS Type: Router Links
  Link State ID: 10.0.1.6
  Advertising Router: P
  LS Seq Number: 80000003

    Link connected to: another Router (point-to-point)
     (Link ID) Neighboring Router ID: 10.0.1.5
     (Link Data) Router Interface address: 10.0.7.30
      Number of MTID metrics: 0
       TOS 0 Metrics: 64

    Link connected to: another Router (point-to-point)
     (Link ID) Neighboring Router ID: 10.0.1.2
     (Link Data) Router Interface address: 10.0.7.18
      Number of MTID metrics: 0
       TOS 0 Metrics: 64

    Link connected to: another Router (point-to-point)
     (Link ID) Neighboring Router ID: 10.0.1.1
     (Link Data) Router Interface address: 10.0.7.10
      Number of MTID metrics: 0
       TOS 0 Metrics: 64 

Primary/Backup Area Border Router designs

In the IP Corner article The OSPF default mysteries I've mentioned the primary/backup ABR design scenarios and provided configuration examples for stub and NSSA areas. For those of you who'd like to see more in-depth design scenarios, I've described a few of them in the Primary/backup area border router designs article in the CT3 wiki.

OSPF in a VRF requires a box-unique router ID

It's obvious why two routers in the same OSPF domain cannot have the same router ID. But requiring unique router IDs on OSPF processes running in different VRFs is probably too harsh, although it does prevent confusion if two VRFs ever get connected through a customer site. Anyhow, if you have overlapping IP addresses on loopback interfaces in different VRFs, OSPF process might not start.

Here's a short example: two VRFs have loopback interfaces with the same IP address. Perfectly legal setup from the MPLS/VPN perspective.

c7200#show ip vrf interfaces
Interface    IP-Address   VRF            Protocol
Lo1003       10.0.0.1     EIGRP_OSPF     up
Lo1001       10.0.0.1     OSPF_1         up

However, when you try to configure the second OSPF process, it fails to start as it cannot get a box-unique router ID. You have to enter a different router ID manually.

c7200#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
c7200(config)#router ospf 1 vrf OSPF_1
c7200(config-router)#network 0.0.0.0 255.255.255.255 area 0
c7200(config-router)#exit
c7200(config)#router ospf 3 vrf EIGRP_OSPF
%OSPF-4-NORTRID: OSPF process 3 cannot pick a router-id.
  Please configure manually or bring up an interface with an ip address.
c7200(config-router)#router-id 10.0.0.2

OSPF area default-cost is a 16-bit quantity

The area area default-cost number router configuration command changes the cost of the default route advertised into a stub or NSSA area. IOS documentation claims the cost is a 24-bit number (and both type-3 and type-5/7 LSAs have 24 bits available for the metric), but in reality the router accepts a 24-bit number and remembers the lower 16 bits, potentially resulting in quite unexpected behavior (just try setting the OSPF costs to 30000 on one ABR and 70000 on another).

Update (2008-08-08): This behavior is reported as bug CSCsl12946 and fixed in IOS release 12.4(20)T.

Here is a sample printout taken from a router running IOS release 12.4(15)T5; 12.2(33)SRC behaves identically.
A1(config)#router ospf 1
A1(config-router)#area 11 default-cost 65538

A1#show running | sect router ospf
router ospf 1
 log-adjacency-changes
 area 11 stub
 area 11 default-cost 2

A1#show ip ospf 1 database summary 0.0.0.0

            OSPF Router with ID (10.0.1.1) (Process ID 1)

                Summary Net Link States (Area 11)

  LS age: 16
  Options: (No TOS-capability, DC, Upward)
  LS Type: Summary Links(Network)
  Link State ID: 0.0.0.0 (summary Network Number)
  Advertising Router: A1
  LS Seq Number: 80000005
  Checksum: 0x4CE0
  Length: 28
  Network Mask: /0
        TOS: 0  Metric: 2 

Shorter display of OSPF database

Recently I had to explore the behavior of Cisco IOS OSPF implementation and had to inspect OSPF database on routers in various areas. If you're only interested in the contents of the database (not in low-level troubleshooting), variety of LSA fields (including LS Age, Options, Checksum, Length ...) are just cluttering the printout, so I fine-tuned the show filter to exclude all the non-relevant fields, ending with show ip ospf database parameters | exclude LS|Options|Check|Len|(MTID:[ 0-9]+$) (the MTID field appears in IOS release 12.2SRC).To make the command more useful, I've changed it into a short Tcl script (using steps from the post explaining how to execute complex CLI commands from Tcl) stored in flash:ospfdb.tcl

set cmd {show ip ospf database }
append cmd $argv
append cmd { | excl LS|Options|Check|Len|(MTID:[ 0-9]+$)}
puts [exec $cmd]
… and defined alias exec ospfdb flash:ospfdb.tcl. I could then easily inspect the contents of various parts of OSPF database I was interested in, for example:
a3#ospfdb external 0.0.0.0
 
            OSPF Router with ID (10.0.1.3) (Process ID 1)
 
                Type-5 AS External Link States
 
  Link State ID: 0.0.0.0 (External Network Number )
  Advertising Router: 10.0.1.5
  Network Mask: /0
        Metric Type: 2 (Larger than any link state path)
        Metric: 1 
        Forward Address: 0.0.0.0
        External Route Tag: 1

The OSPF default mysteries

A while ago I wanted to combine the blog posts I've written about the default routes in OSPF into a single wiki article. As I started to investigate the various options you have to generate default routing in OSPF (including stub areas and not-so-stubby areas), the text quickly became too long and resulted in July IP Corner article "The OSPF default mysteries".

Running OSPF across a PIX/ASA firewall: TTL details

Sharath Samanth has recently asked an interesting question:

I have seen the post on running OSPF across a PIX firewall. Since I did not have a PIX, I tested the solution by replacing PIX with a router.

I had configured the neighbor statements on both routers, but the OSPF was failing to come up. The debug indicated that the router emulating PIX was sending time exceeded ICMP to both OSPF-speaking routers.

The OSPF hello by default has a TTL of 1 which I think is an issue with this scenario. Is there anything special thats done on PIX to get OSPF working?

The answer is quite simple: PIX is not behaving like a router, but rather like a bridge with additional IP features (NAT and traffic filters). It does not decrement the TTL of a transit packet (which could lead to interesting loops if you badly mess up a redundant topology) … and I have to congratulate Sharath for an excellent diagnosis of the problem.

This article is part of You've asked for it series.

OSPF bypasses Control Plane Host Subinterface

I wanted to implement a mechanism that would automatically (using EEM) block unstable OSPF neighbors. Once you identify the neighbors to block (this should be the hard part), blocking them is easy if you're running point-to-point interfaces (you just make the interface passive), but blocking a single neighbor on a multi-access interface is a royal pain. I didn't want to use the access lists, as it would be very hard to integrate OSPF-specific filters with existing incoming access-lists configured on the interfaces. Control Plane Protection looked like the ideal tool to use; if I could drop certain inbound IP packets (OSPF hello packets) based on their source IP address (= unstable neighbor) and IP protocol (= OSPF), they would never get to the OSPF process and the adjacency would not form, resulting in a more stable network.

Update: After the comment from William Chu, I've tested 12.4 mainstream release. OSPF is blocked as configured. Next I've re-read the documentation … and found that one of the documented restrictions is that the host subinterface only filters UDP and TCP traffic. Configuring the service policy on the aggregate path (the control-plane keyword with no options) worked.

Before trying to figure out the integration between the SYSLOG messages and router configuration changes, I performed an easy test: I tried blocking all OSPF traffic in the host control-plane (the one controlling packets received by the IOS processes) with the following configuration:

class-map match-all BlockOSPF
 match access-group name BlockOSPF
!
!
policy-map ControlPlane
 class BlockOSPF
   drop
!
ip access-list extended BlockOSPF
 permit ospf any any
!
control-plane host
 service-policy input ControlPlane
However, according to the show commands, the service policy did not identify any packets as belonging to the BlockOSPF class and the OSPF adjacencies were not affected:
C1#show policy-map control-plane host
 Control Plane Host

  Service-policy input: ControlPlane

    Class-map: BlockOSPF (match-all)
      0 packets, 0 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: access-group name BlockOSPF
      drop

    Class-map: class-default (match-any)
      5 packets, 400 bytes
      5 minute offered rate 0 bps, drop rate 0 bps
      Match: any
C1#show ip ospf neighbor

Neighbor ID Pri State Dead Time Address Interface
10.0.0.12 1 FULL/DR 00:00:30 10.0.1.2 FastEthernet0/0
10.0.0.2 0 FULL/ - 00:00:33 10.0.0.2 Serial1/0.101
10.0.2.2 0 FULL/ - 00:00:33 10.0.0.1 Serial1/0.100
After a few more tests, I had to conclude that the Control Plane Protection using host subinterface does not work on OSPF packets (and it might does not work on other non-TCP/UDP traffic either). Consequently you cannot protect your router from a DoS attack coming through an interface on which you have to run OSPFTo filter non-TCP/UDP traffic, use the aggregate path control plane protection.

OSPF quick learning module

A while ago I've described a scenario where OSPF behaves like a distance-vector protocol, including creating temporary routing black holes. If you think this behavior might affect your network, it's best you test the details in a controlled lab environment. Our OSPF quick learning module will tell you how to tweak the OSPF parameters and how to prevent IP prefix reappearance in the original area. The blended solution also includes a remote lab exercise, where you can test the IOS behavior on actual routers.

E-lessons are subscription-based; you can repeat each module in the lesson (including the lab) as many times as needed.

IPv4 forever?

One of the obscure facts of IPv6 OSPF (OSPFv3) is that it uses a 32-bit router ID like OSPFv2. It's a reasonable choice, I haven't seen an OSPF network with more than a billion routers yet. However, could you guess how this requirement is implemented in Cisco IOS? OSPFv3 searches for an IPv4 address (effectively the same algorithm used by OSPFv2) to get the router ID for the IPv6 routing process. Neat, isn't it?

You might wonder what happens if you want to configure an IPv6-only router. OSPF won't start unless you configure the router ID manually. And, no, you cannot enter a number (which would be the expected format, as the router ID is just a number in the IPv6 world), you have to enter an IPv4 address. Long live IPv4 :))Here is a sample printout from a router. First, let's check the interface status:

Site-D(config)#do show ip interface brief
Interface IP-Address OK? Method Status Protocol
FastEthernet0/0 unassigned YES manual up up
FastEthernet0/1 unassigned YES manual up up
Loopback0 unassigned YES manual up up
No IPv4 running anywhere. Good. Let's continue and configure IPv6:
Site-D(config)#ipv6 unicast
Site-D(config)#ipv6 router ospf 1
Site-D(config-rtr)#
*Mar 1 01:18:46.423: %OSPFv3-4-NORTRID: OSPFv3 process 1 could not pick a router-id,
please configure manually
Oops, IPv6 OSPF won't start if the router doesn't have an IPv4 address. Let's configure the router ID:
Site-D(config-rtr)#router-id ?
A.B.C.D OSPF router-id in IP address format

Site-D(config-rtr)#router-id 10.0.1.8
Site-D(config-rtr)#

If you're a Cisco partner, you can run the Configuring OSPFv3 remote lab exercise free-of-charge from Partner Education Connection. Everyone else can get the same exercise as part of the IPv6 remote lab bundle from our learning store, where you can also buy the IPv6 Fundamentals, Design and Deployment (IP6FD) e-course.

AI (Artificial Intelligence) in Cisco IOS release 12.4T

Years ago, when the "fourth-generation computers" (looks like they've been renumbered to the fifth generation in the meantime) were as hot as Web 2.0 is today, we started talking about the generation after that which you could control with statements like “Do what I don't know how” ... and some of that mentality is already built into Cisco IOS.

In a comment to my recent NTP-related post mentioning OSPF configuration, Wan Tajuddin correctly stated that the OSPF network statement should contain the wildcard bits, not the subnet mask. But I was positive I had running networks with the network 0.0.0.0 0.0.0.0 area 0 OSPF configuration, so it was time for one more lab test. As it turns out, at least the IOS release 12.4T accepts either the wildcard bits or the subnet mask and figures out which one you've used.Here is the printout from the test run. First the traditional configuration:

R1#show run ¦ sect router ospf
router ospf 1
log-adjacency-changes
network 0.0.0.0 255.255.255.255 area 0
R1#show ip ospf interface brief
Interface PID Area IP Address/Mask Cost State Nbrs F/C
Lo0 1 0 10.0.1.1/32 1 LOOP 0/0
Se1/1 1 0 10.0.7.5/30 70 P2P 1/1
Se1/0 1 0 10.0.7.1/30 64 P2P 1/1
Fa0/0 1 0 10.0.0.1/24 10 WAIT 0/0
The configuration uses the wildcard bits and all interfaces are in area 0 as expected. Now let's try to specify the 0.0.0.0/0 network:
R1#conf t
Enter configuration commands, one per line. End with CNTL/Z.
R1(config)#no router ospf 1
Mar 1 00:09:37.640: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.3 on Serial1/1 from FULL to DOWN, Neighbor Down: Interface down or detached
Mar 1 00:09:37.648: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.2 on Serial1/0 from FULL to DOWN, Neighbor Down: Interface down or detached
R1(config)#router ospf 1
R1(config-router)#network 0.0.0.0 0.0.0.0 area 0
R1(config-router)#^Z
R1#
Mar 1 00:09:50.944: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.3 on Serial1/1 from LOADING to FULL, Loading Done
Mar 1 00:09:51.104: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.2 on Serial1/0 from LOADING to FULL, Loading Done
R1#show ip ospf interface brief
Interface PID Area IP Address/Mask Cost State Nbrs F/C
Lo0 1 0 10.0.1.1/32 1 LOOP 0/0
Se1/1 1 0 10.0.7.5/30 70 P2P 1/1
Se1/0 1 0 10.0.7.1/30 64 P2P 1/1
Fa0/0 1 0 10.0.0.1/24 10 WAIT 0/0
OK, it works for the default route, how about a specific subnet (10.0.7.0/24):
R1#conf t
Enter configuration commands, one per line. End with CNTL/Z.
R1(config)#no router ospf 1
Mar 1 00:09:37.640: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.3 on Serial1/1 from FULL to DOWN, Neighbor Down: Interface down or detached
Mar 1 00:09:37.648: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.2 on Serial1/0 from FULL to DOWN, Neighbor Down: Interface down or detached
R1(config)#router ospf 1
R1(config-router)#network 10.0.7.0 255.255.255.0 area 0
R1(config-router)#^Z
R1#
Mar 1 00:09:50.944: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.3 on Serial1/1 from LOADING to FULL, Loading Done
Mar 1 00:09:51.104: %OSPF-5-ADJCHG: Process 1, Nbr 10.0.1.2 on Serial1/0 from LOADING to FULL, Loading Done
R1#show ip ospf interface brief
Interface PID Area IP Address/Mask Cost State Nbrs F/C
Se1/1 1 0 10.0.7.5/30 70 P2P 1/1
Se1/0 1 0 10.0.7.1/30 64 P2P 1/1
As expected, only the WAN interfaces with IP addresses in the specified IP address range are in the OSPF process, so IOS properly recognizes IP prefix specifications including subnet masks. OK, but let's see how the router stores the OSPF configuration:
R1#show running-config ¦ section router ospf
router ospf 1
log-adjacency-changes
network 10.0.7.0 0.0.0.255 area 0
Cool. The router automatically inverts the subnet mask into wildcard bits, so even if you use the “wrong” format on the CCIE lab, the automatic grading program will get the expected results :)

Mixing OSPF network types on the same WAN IP subnet

A few days ago, I've published a link to a series of OSPF-over-WAN tutorials by Arden Packeer. He went a step further and analyzed what happens if you mix supposedly incompatible OSPF network types in the same IP subnet.

Running OSPF across Frame Relay

The various models you can use to run OSPF over partially-meshed WAN (usually Frame Relay) are complex enough to make you scream ... or switch to EIGRP :) Arden Packeer did an excellent job in his three-part tutorial describing broadcast and non-broadcast network types, point-to-multipoint network type and point-to-point links over Frame Relay.

If you still have problems with OSPF after reading all three tutorials, use point-to-point subinterfaces.

Routing protocol redistribution

Routing protocol redistribution is one of those tough Cisco IOS topics that everyone struggles with sooner or later. To ease your path to mastering the redistribution issues, Petr Lapuhkov wrote two articles: the theory of route redistribution and a simple case study.

Building Core Networks with BGP, OSPF and MPLS

Do you need advanced knowledge and skills needed in designing and implementing core MPLS networks? We have developed exactly the course you need.

The Building Core Networks with BGP, OSPF and MPLS (NIL_BCMPL) course focuses on MPLS applications such as MPLS VPN (with special attention to Internet access from a VPN), Any Transport over MPLS (AToM), Carrier Supporting Carrier (CsC) and MPLS Traffic Engineering (MPLS TE). As a prerequisite for MPLS deployment the routing protocols are explained as well - the Open Shortest Path First (OSPF) and Border Gateway Protocol (BGP). The scalability issues of the protocols as well as multiprotocol BGP are addressed as well. Several practice labs enable you to gain the necessary experience in deploying the MPLS-based core networks.

Common sense prevails over RFC 2328

When trying to extract the OSPF route selection rules from RFC 2328, I've stumbled across a very weird rule (section 16.4.1): if an ASBR within a non-backbone area advertises an external route (or if the forwarding address is within the non-backbone area), it's preferred over external routes advertised by ASBRs in other areas regardless of its metric. I simply had to test this on Cisco IOS … and found out that Cisco engineers prefer common sense to OSPF RFC.I've built a sample network where two routers (10.0.0.11 in area 1, 10.0.0.3 in the backbone area) advertise the same external routes. The router in area 1 advertises the routes with a higher metric:

S2#ospfExternals
 
External OSPF routes for OSPF process ID 1
 
  Prefix Cost Tag ASBR Forward addr
==================================================================
> 10.1.0.1/32 10 E1 1 10.0.0.3
  10.1.0.1/32 2000 E1 1 10.0.0.11
> 10.1.0.2/32 5 E2 2 10.0.0.3
  10.1.0.2/32 200 E2 2 10.0.0.11
You can use the show ip ospf route command to verify which ASBR is within the area. An abbreviated printout is included:
S2#show ip ospf route
 
    Intra-area Router Path List
i 10.0.0.11 [64] via 10.0.2.17, Serial1/2, ASBR, Area 1, SPF 2
i 10.0.0.2 [64] via 10.0.2.13, Serial1/1, ABR, Area 1, SPF 2
i 10.0.0.1 [64] via 10.0.2.5, Serial1/0, ABR, Area 1, SPF 2
 
    Inter-area Router Path List
I 10.0.0.3 [128] via 10.0.2.5, Serial1/0, ASBR, Area 1, SPF 2
As you can see, 10.0.0.11 is within area 1, whereas 10.0.0.3 is in another area. Still, IOS prefers external routes advertised by 10.0.0.3 due to their lower metric:
S2#show ip ospf route
    External Route List
*> 10.1.0.1/32, Ext1, cost 138, tag 1
      via 10.0.2.5, Serial1/0
*> 10.1.0.2/32, Ext2, cost 5, tag 2
      via 10.0.2.5, Serial1/0

Configure OSPF on unnumbered interfaces

When we've been assigning router interfaces in OSPF areas with the network router configuration command, it was impossible to start OSPF only on some unnumbered interfaces and not on others (or place the unnumbered interfaces in different areas). These restrictions are removed if you use the ip ospf area interface configuration command. For example, to put the loopback interface into another area than the WAN links using its IP address, use the following configuration commands:

router ospf 1
!
interface Loopback0
 ip address 10.0.0.3 255.255.255.255
 ip ospf 1 area 0
!
interface Serial1/0
 ip unnumbered Loopback0
 ip ospf 1 area 1
!
interface Serial1/1
 ip unnumbered Loopback0
 ip ospf 1 area 2

Use slow IGP startup in LDP-only MPLS environments

If you use LDP-based MPLS as the only means of transporting data across your network core (for example, in MPLS VPN networks or in BGP-free ISP core), a router startup might disrupt your Label Switched Paths (remember: they are always based on IGP best paths) leading to temporary disruption in service.For example, when the router P1 in the network shown in the following diagram is powered on and its IGP advertises its presence, the IGP-derived path from PE1 to PE2 will go over P1. If the LDP on P1 has not exchanged labels with PE1 and PE2, there will be no LSP on the shortest path between PE1 and PE2, resulting in a loss of traffic until the labels are exchanged and LSP is built.The proper router startup timing in this environment is thus:

  • Start IGP and find neighbors.
  • Receive IGP updates and build the network topology.
  • Start LDP and exchange labels for all prefixes in the network.
  • Advertise router's presence in IGP.
You can configure slow OSPF startup with the max-metric router-lsa on-startup seconds router configuration command. The corresponding IS-IS command is set-overload-bit on-startup seconds.

The initial IGP delay has to be configured manually (you cannot use wait-for-bgp option in this scenario) and should take in account the time needed to:
  • Find IGP neighbors (at least the hello timer);
  • Receive LSA updates;
  • Run SPF (at least the spf delay).
  • Find LDP neighbors (at least the discovery hello interval).
  • Exchange labels once the SPF run has completed.

Unless you're under very rigid time constraints, 30 seconds seems like a reasonable delay in most environments.

When OSPF Becomes a Distance Vector Protocol

A while ago I've read an interesting post where Jeff Doyle, while describing his new-hire interview process, mentioned that inter-area OSPF is actually a distance vector routing protocol. At the time I found the argument interesting, but a bit academic.

A few months later, I've experienced interesting routing flaps in one of my lab setups. A detailed investigation revealed that they were caused by Area Border Routers redistributing inter-area information back into the area from which it originated, effectively creating a temporary routing black hole. You can find the details, in-depth explanation as well as workaround solutions in my february IP Corner article When OSPF Becomes a Distance Vector Protocol.

OSPF default route based on IP SLA

Olivier Guillemain has asked an interesting question: “how could I originate a default route into OSPF based on IP SLA (for example, based on pinging a remote IP address)?

This is very easy to do when the router originating the default route into OSPF needs an SLA-based default route itself:

  1. Configure IP SLA and a corresponding track object;
  2. Configure a default route using reliable static routing
  3. Advertise the default route into OSPF with the default-information originate router configuration command

The solution is a bit more complex when the router originating the default route into OSPF should not have a default route. In this case, you could use a routing trick:

  1. Configure IP SLA and a corresponding track object as before;
  2. Use reliable static routing to configure a static host route for a bogus IP address (for example, 10.0.0.1/32) pointing to null0 (for example, ip route 10.0.0.1 255.255.255.255 null 0 track 100). Obviously this host route should not be redistributed into any routing protocol.
  3. Conditionally advertise default route into OSPF based on presence of the static host route.

Note: This article is part of You've asked for it series.