/*
Spam cleverly-constructed fake filter requests to block "from". These should be
dropped by the router, as they do not have legitimate nonces.
*/
func sendFakeRequests(from, to net.IP) {
	req := filter.Request{
		Type:  filter.FilterReq,
		SrcIP: to,
		DstIP: from,
		Flow: routerecord.RouteRecord{
			Protocol: byte(layers.IPProtocolTCP),
			Path: []routerecord.Router{
				{
					IP:    net.ParseIP("10.4.32.3"),
					Nonce: [8]byte{5, 5, 5, 5, 5, 5, 5, 5},
				},
				{
					IP:    net.ParseIP("10.4.32.2"),
					Nonce: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
				},
			},
		},
	}

	for _ = range time.Tick(time.Second) {
		log.Println("Sending illigitimate filter request:", req)
		req.Send(req.Flow.Path[0].IP)
	}
}
/*listenForRouteRecords intercepts all incoming packets to this host and
removes their route records before letting the operating system process them.

If listenForRouteRecords is true, it also sends filter requests whenever an
ICMP packet from 10.4.32.4 arrives.
*/
func listenForRouteRecords(sendFilterRequests bool) {
	routerecord.Init()

	nfq, err := netfilter.NewNFQueue(0, 100000, 0xffff)
	if err != nil {
		log.Fatal(err)
	}

	defer nfq.Close()

	for packet := range nfq.GetPackets() {
		var ipLayer *layers.IPv4

		if layer := packet.Packet.Layer(layers.LayerTypeIPv4); layer != nil {
			ipLayer = layer.(*layers.IPv4)
		} else {
			packet.SetVerdict(netfilter.NF_ACCEPT)
			continue
		}

		if routerecord.Shimmed(ipLayer) {
			/*If the IP layer has a shim, remove it.*/
			log.Println("Got AITF shimmed packet from", aitf.Hostname(ipLayer.SrcIP))
			rr := routerecord.Unshim(ipLayer)
			log.Println(rr)

			if sendFilterRequests {
				/*If this is a malicious packet, construct a filter request to stop any
				future undesired traffic from this flow. The policy module simply
				considers any ICMP traffic from the attacker to be "malicous".*/
				if ipLayer.Protocol == layers.IPProtocolICMPv4 && ipLayer.SrcIP.Equal(net.ParseIP("10.4.32.4")) {
					log.Println("Malicious packet detected from", aitf.Hostname(ipLayer.SrcIP))

					req := filter.Request{
						Type:  filter.FilterReq,
						SrcIP: ipLayer.SrcIP,
						DstIP: ipLayer.DstIP,
						Flow:  *rr,
					}
					req.Send(rr.Path[len(rr.Path)-1].IP)
				}
			}

			/*Serialize the IP packet. Assuming this is successful, accept it.*/
			b, err := routerecord.Serialize(ipLayer)
			if err != nil {
				log.Println(err)
				packet.SetVerdict(netfilter.NF_DROP)
			} else {
				packet.SetResult(netfilter.NF_ACCEPT, b)
			}
		} else {
			/*Any packets without a shim can be accepted as-is.*/
			log.Println("Got", ipLayer.Protocol, "packet from", aitf.Hostname(ipLayer.SrcIP))
			packet.SetVerdict(netfilter.NF_ACCEPT)
		}
	}
}
/*
listenForFilterRequest waits for a filter request from a client to come.  Then,
it verifies the authenticity of the request and takes the appropriate action
based on the AITF filter request protocol.
*/
func listenForFilterRequest(mode complianceMode) {
	if handshakes == nil {
		handshakes = make(map[uint64](*filter.Request))
	}

	/*Open up a UDP server on the filter request port and handle any messages
	that arrive.*/
	serverAddr, err := net.ResolveUDPAddr("udp", ":54321")
	if err != nil {
		log.Fatal(err)
	}

	serverConn, err := net.ListenUDP("udp", serverAddr)
	if err != nil {
		log.Fatal(err)
	}

	defer serverConn.Close()

	buf := make([]byte, 5000)
	for {
		n, addr, _ := serverConn.ReadFromUDP(buf)

		/*Read a request from the UDP connection*/
		var req filter.Request
		_, err := req.ReadFrom(bytes.NewBuffer(buf[:n]))
		if err != nil {
			log.Println(err)
			continue
		}

		/*Throw the request away if it is not authentic.*/
		if !req.Authentic() {
			log.Println("Received a forged filter request!")
			continue
		}

		log.Println("Got", req.Type, "from", aitf.Hostname(addr.IP))

		switch req.Type {
		case filter.FilterReq:
			/*When we receive a filter request, install a temporary filter and begin
			a counter-connection with the attacker's router. Also let the victim know
			that the attack should have stopped with a filter ACK. The filter is
			installed for the full filter time instead of the temporary time, but is
			automatically removed when we get a filter ACK.*/
			filter.InstallFilter(req, filter.LongFilterTime, true)
			req.Type = filter.FilterAck
			req.Send(addr.IP)

			/*If we've blocked this filter before and it's still happening, escalate
			the filter and just block it here.*/
			for _, req2 := range shadowFilters {
				if req2.SrcIP.Equal(req.SrcIP) && req2.DstIP.Equal(req.DstIP) {
					log.Println("Escalating request.")
					return
				}
			}

			req.Type = filter.CounterConnectionSyn
			req.Send(req.Flow.Path[0].IP)

		case filter.CounterConnectionSyn:
			if mode == comply || mode == lie {
				/*When we get a counter-connection SYN, continue the three-way handshake
				with a SYN-ACK. We don't install a filter until we get an ACK back with
				the right nonce.*/
				req.Type = filter.CounterConnectionSynAck
				req.Nonce = uint64(rand.Int63())
				handshakes[req.Nonce] = &req
				req.Send(addr.IP)
			}

		case filter.CounterConnectionSynAck:
			if mode == comply || mode == lie {
				/*When we receive a response to a counter-connection, complete the
				three-way handshake.*/
				req.Type = filter.CounterConnectionAck
				req.Send(addr.IP)
			}

		case filter.CounterConnectionAck:
			if mode == comply || mode == lie {
				/*When we receive a counter-connection ACK, make sure we're actually
				waiting for a response to that handshake, then install a temporary
				filter.*/
				originalReq := handshakes[req.Nonce]
				if originalReq == nil {
					log.Println("Received a forged three-way handshake:", req)
					continue
				} else {
					delete(handshakes, req.Nonce)
				}
			}

			if mode == comply {
				/*The filter is installed for the full filter time instead of the
				temporary time, but is automatically removed when we get a filter ACK.*/
				filter.InstallFilter(req, filter.LongFilterTime, true)

				/*The attacker should be informed of its wrongdoing, and the victim's
				router should be informed that this router is complying with the
				request.*/
				req.Type = filter.FilterReq
				req.Send(req.SrcIP)
				req.Type = filter.FilterAck
				req.Send(addr.IP)
			}

		case filter.FilterAck:
			if mode == comply {
				/*When we get acknowledgement of compliance with a filter, we can remove
				our temporary filter. Nobody lies on the internet.*/
				filter.UninstallFilter(req, true)

				shadowFilters = append(shadowFilters, req)
			}
		}
	}
}
/*
listenForFilterRequest waits for a filter request from a router to come.  Then,
it verifies the authenticity of the request and takes some action.

If action is comply, it filters the attack.

If action is ignore, it logs the request but does nothing about it.comply

If action is lie, it complies with the request but doesn't actually add a filter.
*/
func listenForFilterRequest(mode complianceMode) {
	/*Open up a UDP server on the filter request port and handle any messages
	that arrive.*/
	serverAddr, err := net.ResolveUDPAddr("udp", ":54321")
	if err != nil {
		log.Fatal(err)
	}

	serverConn, err := net.ListenUDP("udp", serverAddr)
	if err != nil {
		log.Fatal(err)
	}

	defer serverConn.Close()

	buf := make([]byte, 5000)
	for {
		n, addr, _ := serverConn.ReadFromUDP(buf)

		/*Read a request from the UDP connection*/
		var req filter.Request
		_, err := req.ReadFrom(bytes.NewBuffer(buf[:n]))
		if err != nil {
			log.Println(err)
			continue
		}

		log.Println("Got", req.Type, "from", aitf.Hostname(addr.IP))
		switch req.Type {
		case filter.FilterReq:
			switch mode {
			case comply:
				log.Println("Complying with filter request...")

				/*If this host is okay with filter requests, add a firewall rule to
				block the requested flow and respond with an acknowledgement.*/
				filter.InstallFilter(req, filter.LongFilterTime, false)
				req.Type = filter.FilterAck
				req.Send(addr.IP)

			case ignore:
				log.Println("Ignoring filter request...")

			case lie:
				log.Println("Pretending to comply with filter request...")

				/*If this host is a lier, send an acknowledgement without actually
				installing a filter rule.*/
				req.Type = filter.FilterAck
				req.Send(addr.IP)
			}

		case filter.FilterAck:
			/*Do nothing.  The routers should take care of mitigating the attack from
			now on.*/

		default:
			/*Hosts shouldn't get any of the other message types.*/
			log.Println("Unexpected filter request:", req)
		}
	}
}