kernel.c

来自「ipsec vpn」· C语言 代码 · 共 2,050 行 · 第 1/5 页

C
2,050
字号
/* routines that interface with the kernel's IPsec mechanism * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998-2002  D. Hugh Redelmeier. * * 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.  See <http://www.fsf.org/copyleft/gpl.txt>. * * 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. * * RCSID $Id: kernel.c,v 1.232.2.1 2005/10/21 02:50:46 ken Exp $ */#include <stddef.h>#include <string.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <wait.h>#include <unistd.h>#include <fcntl.h>#include <sys/utsname.h>#include <sys/queue.h>#include <sys/stat.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <openswan.h>#include <openswan/ipsec_policy.h>#ifdef KLIPS#include <signal.h>#include <sys/time.h>   /* for select(2) */#include <sys/types.h>  /* for select(2) */#include <pfkeyv2.h>#include <pfkey.h>#include "kameipsec.h"#endif /* KLIPS */#include "constants.h"#include "oswlog.h"#include "defs.h"#include "rnd.h"#include "id.h"#include "connections.h"        /* needs id.h */#include "state.h"#include "timer.h"#include "kernel.h"#include "kernel_netlink.h"#include "kernel_pfkey.h"#include "kernel_noklips.h"#include "packet.h"#include "x509.h"#include "log.h"#include "server.h"#include "whack.h"      /* for RC_LOG_SERIOUS */#include "keys.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif#ifdef NAT_TRAVERSAL#include "packet.h"  /* for pb_stream in nat_traversal.h */#include "nat_traversal.h"#endifbool can_do_IPcomp = TRUE;  /* can system actually perform IPCOMP? *//* test if the routes required for two different connections agree * It is assumed that the destination subnets agree; we are only * testing that the interfaces and nexthops match. */#define routes_agree(c, d) ((c)->interface->ip_dev == (d)->interface->ip_dev \        && sameaddr(&(c)->spd.this.host_nexthop, &(d)->spd.this.host_nexthop))/* bare (connectionless) shunt (eroute) table * * Bare shunts are those that don't "belong" to a connection. * This happens because some %trapped traffic hasn't yet or cannot be * assigned to a connection.  The usual reason is that we cannot discover * the peer SG.  Another is that even when the peer has been discovered, * it may be that no connection matches all the particulars. * We record them so that, with scanning, we can discover * which %holds are news and which others should expire. */#define SHUNT_SCAN_INTERVAL     (60 * 2)   /* time between scans of eroutes *//* SHUNT_PATIENCE only has resolution down to a multiple of the sample rate, * SHUNT_SCAN_INTERVAL. * By making SHUNT_PATIENCE an odd multiple of half of SHUNT_SCAN_INTERVAL, * we minimize the effects of jitter. */#define SHUNT_PATIENCE  (SHUNT_SCAN_INTERVAL * 15 / 2)  /* inactivity timeout */struct bare_shunt {    policy_prio_t policy_prio;    ip_subnet ours;    ip_subnet his;    ip_said said;    int transport_proto;    unsigned long count;    time_t last_activity;    char *why;    struct bare_shunt *next;};static struct bare_shunt *bare_shunts = NULL;#ifdef DEBUGstatic voidDBG_bare_shunt(const char *op, const struct bare_shunt *bs){    DBG(DBG_KLIPS,        {            int ourport = ntohs(portof(&(bs)->ours.addr));            int hisport = ntohs(portof(&(bs)->his.addr));            char ourst[SUBNETTOT_BUF];            char hist[SUBNETTOT_BUF];            char sat[SATOT_BUF];            char prio[POLICY_PRIO_BUF];            subnettot(&(bs)->ours, 0, ourst, sizeof(ourst));            subnettot(&(bs)->his, 0, hist, sizeof(hist));            satot(&(bs)->said, 0, sat, sizeof(sat));            fmt_policy_prio(bs->policy_prio, prio);            DBG_log("%s bare shunt %p %s:%d -%d-> %s:%d => %s %s    %s"                , op, (const void *)(bs), ourst, ourport, (bs)->transport_proto, hist, hisport                , sat, prio, (bs)->why);        });}#else /* !DEBUG */#define DBG_bare_shunt(op, bs) {}#endif /* !DEBUG *//* The orphaned_holds table records %holds for which we * scan_proc_shunts found no representation of in any connection. * The corresponding ACQUIRE message might have been lost. */struct eroute_info *orphaned_holds = NULL;/* forward declaration */static bool shunt_eroute(struct connection *c                         , struct spd_route *sr                         , enum routing_t rt_kind                         , unsigned int op, const char *opname);static void set_text_said(char *text_said                          , const ip_address *dst                          , ipsec_spi_t spi                          , int proto);static const struct pfkey_proto_info narrow_proto_info[2] = {        {                proto: IPPROTO_ESP,                encapsulation: ENCAPSULATION_MODE_TRANSPORT,                reqid: 0        },        {                proto: 0,                encapsulation: 0,                reqid: 0        }};static const struct pfkey_proto_info broad_proto_info[2] = {        {                proto: IPPROTO_ESP,                encapsulation: ENCAPSULATION_MODE_TUNNEL,                reqid: 0        },        {                proto: 0,                encapsulation: 0,                reqid: 0        }};voidrecord_and_initiate_opportunistic(const ip_subnet *ours                                  , const ip_subnet *his                                  , int transport_proto                                  , const char *why){    passert(samesubnettype(ours, his));    /* Add to bare shunt list.     * We need to do this because the shunt was installed by KLIPS     * which can't do this itself.     */    {        struct bare_shunt *bs = alloc_thing(struct bare_shunt, "bare shunt");        bs->why = clone_str(why, "story for bare shunt");        bs->ours = *ours;        bs->his = *his;        bs->transport_proto = transport_proto;        bs->policy_prio = BOTTOM_PRIO;        bs->said.proto = SA_INT;        bs->said.spi = htonl(SPI_HOLD);        bs->said.dst = *aftoinfo(subnettypeof(ours))->any;        bs->count = 0;        bs->last_activity = now();        bs->next = bare_shunts;        bare_shunts = bs;        DBG_bare_shunt("add", bs);    }    /* actually initiate opportunism */    {        ip_address src, dst;        networkof(ours, &src);        networkof(his, &dst);        initiate_opportunistic(&src, &dst, transport_proto, TRUE, NULL_FD, "acquire");    }    /*     * if present, remove from orphaned_holds list.     * NOTE: we do this last in case ours or his is a pointer into a member.     */    {        struct eroute_info **pp, *p;        for (pp = &orphaned_holds; (p = *pp) != NULL; pp = &p->next)        {            if (samesubnet(ours, &p->ours)            && samesubnet(his, &p->his)            && transport_proto == p->transport_proto            && portof(&ours->addr) == portof(&p->ours.addr)            && portof(&his->addr) == portof(&p->his.addr))            {                *pp = p->next;                pfree(p);                break;            }        }    }}static unsigned get_proto_reqid(unsigned base, int proto){    switch (proto)    {    default:    case IPPROTO_COMP:        base++;        /* fall through */    case IPPROTO_ESP:        base++;        /* fall through */    case IPPROTO_AH:        break;    }    return base;}/* Generate Unique SPI numbers. * * The specs say that the number must not be less than IPSEC_DOI_SPI_MIN. * Pluto generates numbers not less than IPSEC_DOI_SPI_OUR_MIN, * reserving numbers in between for manual keying (but we cannot so * restrict numbers generated by our peer). * XXX This should be replaced by a call to the kernel when * XXX we get an API. * The returned SPI is in network byte order. * We use a random number as the initial SPI so that there is * a good chance that different Pluto instances will choose * different SPIs.  This is good for two reasons. * - the keying material for the initiator and responder only *   differs if the SPIs differ. * - if Pluto is restarted, it would otherwise recycle the SPI *   numbers and confuse everything.  When the kernel generates *   SPIs, this will no longer matter. * We then allocate numbers sequentially.  Thus we don't have to * check if the number was previously used (assuming that no * SPI lives longer than 4G of its successors). */ipsec_spi_tget_ipsec_spi(ipsec_spi_t avoid, int proto, struct spd_route *sr, bool tunnel){    static ipsec_spi_t spi = 0; /* host order, so not returned directly! */    char text_said[SATOT_BUF];    set_text_said(text_said, &sr->this.host_addr, 0, proto);    if (kernel_ops->get_spi)        return kernel_ops->get_spi(&sr->that.host_addr            , &sr->this.host_addr, proto, tunnel            , get_proto_reqid(sr->reqid, proto)            , IPSEC_DOI_SPI_OUR_MIN, 0xffffffff            , text_said);    spi++;    while (spi < IPSEC_DOI_SPI_OUR_MIN || spi == ntohl(avoid))        get_rnd_bytes((u_char *)&spi, sizeof(spi));    DBG(DBG_CONTROL,        {            ipsec_spi_t spi_net = htonl(spi);            DBG_dump("generate SPI:", (u_char *)&spi_net, sizeof(spi_net));        });    return htonl(spi);}/* Generate Unique CPI numbers. * The result is returned as an SPI (4 bytes) in network order! * The real bits are in the nework-low-order 2 bytes. * Modelled on get_ipsec_spi, but range is more limited: * 256-61439. * If we can't find one easily, return 0 (a bad SPI, * no matter what order) indicating failure. */ipsec_spi_tget_my_cpi(struct spd_route *sr, bool tunnel){    static cpi_t        first_busy_cpi = 0,        latest_cpi;    char text_said[SATOT_BUF];    set_text_said(text_said, &sr->this.host_addr, 0, IPPROTO_COMP);    if (kernel_ops->get_spi)        return kernel_ops->get_spi(&sr->that.host_addr            , &sr->this.host_addr, IPPROTO_COMP, tunnel            , get_proto_reqid(sr->reqid, IPPROTO_COMP)            , IPCOMP_FIRST_NEGOTIATED, IPCOMP_LAST_NEGOTIATED            , text_said);    while (!(IPCOMP_FIRST_NEGOTIATED <= first_busy_cpi && first_busy_cpi < IPCOMP_LAST_NEGOTIATED))    {        get_rnd_bytes((u_char *)&first_busy_cpi, sizeof(first_busy_cpi));        latest_cpi = first_busy_cpi;    }    latest_cpi++;    if (latest_cpi == first_busy_cpi)        find_my_cpi_gap(&latest_cpi, &first_busy_cpi);    if (latest_cpi > IPCOMP_LAST_NEGOTIATED)        latest_cpi = IPCOMP_FIRST_NEGOTIATED;    return htonl((ipsec_spi_t)latest_cpi);}static booldo_command(struct connection *c, struct spd_route *sr, const char *verb, struct state *st){    const char *verb_suffix;    /* figure out which verb suffix applies for logging purposes */    {        const char *hs, *cs;        switch (addrtypeof(&sr->this.host_addr))        {            case AF_INET:                hs = "-host";                cs = "-client";                break;            case AF_INET6:                hs = "-host-v6";                cs = "-client-v6";                break;            default:                loglog(RC_LOG_SERIOUS, "unknown address family");                return FALSE;        }        verb_suffix = subnetisaddr(&sr->this.client, &sr->this.host_addr)            ? hs : cs;    }    DBG(DBG_CONTROL, DBG_log("command executing %s%s"			     , verb, verb_suffix));    if(kernel_ops->docommand != NULL) {	return (*kernel_ops->docommand)(c,sr, verb, st);    } else {	DBG(DBG_CONTROL, DBG_log("no do_command for method %s", kernel_ops->opname));    }    return TRUE;}/* Check that we can route (and eroute).  Diagnose if we cannot. */enum routability {    route_impossible = 0,    route_easy = 1,    route_nearconflict = 2,    route_farconflict = 3};static enum routabilitycould_route(struct connection *c)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?