isdn_timru.c
/* isdn_timru.c.shtml,v 1.1.1.1 2002/08/19 05:50:13 fritz Exp
*
* Linux ISDN subsystem, timeout-rules for network interfaces.
*
* Copyright 1997 by Christian Lademann <cal@zls.de>
*
* 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, 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; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* isdn_timru.c.shtml,v
* Revision 1.1.1.1 2002/08/19 05:50:13 fritz
* - initial import
*
* Revision 1.2 1998/03/07 23:17:28 fritz
* Added RCS keywords
* Bugfix: Did not compile without isdn_dumppkt beeing enabled.
*
*/
/*
02.06.97:cal:
- ISDN_TIMRU_PACKET_NONE = 0 definiert, die anderen ISDN_TIMRU_PACKET_* -
Definitionen jeweils inkrementiert
- isdn_net_recalc_timeout():
- In der Schleife zum Finden einer passenden Regel wurde in jedem Fall
die Wildcard-Kette durchsucht. Jetzt nicht mehr.
- beim Testen einer Bringup-Regel wird der anfaengliche Timeout auf den
Hangup-Timeout des Devices gesetzt (lp->onhtime).
10.06.97:cal:
- isdn_net_recalc_timeout(): rule->neg-Handling gesaeubert: eine Regel passt
genau dann, wenn match(rule) XOR rule->neg und das bei allen Regeltypen.
- isdn_net_add_rule(): rule->timeout bei BRINGUP immer 1, sonst > 0.
- alle return(-1), die zu ioctl-Calls zurueckgehen --> return(-EINVAL).
- div. Leerzeilen geloescht / eingefuegt; alle return's "geklammert".
12.06.97:cal:
- isdn_net_recalc_timeout(): Falls IP-Masquerading verwendet wird, kann mit
der neuen Option CONFIG_TIMRU_USE_MASQ der Regel-Match auf die ursprueng-
lichen Adressen und nicht auf die der Firewall angewendet werden. Dazu ist
ein Patch in net/ipv4/ip_masq.c notwendig: ip_masq_in_get_2 muss
exportiert werden.
26.06.97:cal:
- isdn_net_add_rule(): rule->timeout darf bei BRINGUP >= 0 sein. Damit
laesst sich folgende Systax erreichen: "Falls Paket passt, starte die
Verbindung NICHT", wie es im Stand 970602 moeglich war.
- isdn_net_recalc_timeout(): BRINGUP: initial timeout wird auf den in der
passenden Regel gefundenen Timeout gesetzt. Ist dieser 0, so wird die
Verbindung nicht aufgebaut.
16.10.97:cal:
- isdn_net_recalc_timeout(): beachte Fake-Header, der bei ausgehenden
SyncPPP-Paketen eingesetzt wird;
TimRu's "recalc timeout:" - Meldungen in einer Zeile
04.11.97:cal:
- isdn_net.c, isdn_net_new(): Timeout-Rules nicht mehr automatisch
alloziieren;
- isdn_net.c, isdn_net_autohup(): AutoHup auch durchfuehren, wenn keine
Timeout-Rules alloziiert sind.
*/
/*
TODO:
- Masq-Adressen statt Paketadresse ausgeben, falls die Masq-Adressen verwendet
werden.
- Masq-Adressen-Verwendung als Option in den Regeln vorsehen
- TCP-Flags als Regel-Optionen
- weitere Verfeinerungen fuer Nicht-TCP/IP-Pakete
*/
#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/isdn.h>
#include <linux/if_arp.h>
#include <linux/in.h>
#include <net/icmp.h>
#include <net/tcp.h>
#include <net/udp.h>
#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
#ifdef CONFIG_IP_MASQUERADE
#include <net/ip_masq.h>
#endif
#endif
#include "isdn_common.h"
#include "isdn_net.h"
#ifdef CONFIG_ISDN_PPP
#include "isdn_ppp.h"
#endif
#ifdef CONFIG_ISDN_TIMEOUT_RULES
static int
isdn_timru_match(isdn_timeout_rule *this, isdn_timeout_rule *rule);
#define printk_ip(a) printk("%ld.%ld.%ld.%ld",(ntohl(a)>>24)&0xFF,\
(ntohl(a)>>16)&0xFF,\
(ntohl(a)>>8)&0xFF,\
(ntohl(a))&0xFF)
#define printk_port(a) printk("%d",ntohs(a))
#define VERBOSE_PRINTK(v, l, p...) { \
if(dev->net_verbose >= (v)) { \
printk(l ## p); \
} else { ; } \
}
int
isdn_net_recalc_timeout(int type, int prot, struct device *ndev, void *buf, ulong arg) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
struct sk_buff *skb;
struct iphdr *ip;
struct icmphdr *icmp;
struct tcphdr *tcp;
struct udphdr *udp;
#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
#ifdef CONFIG_IP_MASQUERADE
struct ip_masq *masq;
int m_prot;
__u32 m_saddr, m_daddr;
__u16 m_sport, m_dport;
int check_for_masq;
#endif
#endif
isdn_timeout_rule match_rule;
/*
char *cbuf;
*/
int ppp_proto, ppp_hdrlen = 0, new_timeout;
match_rule.type = type;
match_rule.protfam = ISDN_TIMRU_PROTFAM_WILDCARD;
if(dev->net_verbose > 4) {
printk(KERN_DEBUG "recalc_timeout:");
switch(type) {
case ISDN_TIMRU_BRINGUP: printk("BRINGUP, "); break;
case ISDN_TIMRU_KEEPUP_IN: printk("KEEPUP_IN, "); break;
case ISDN_TIMRU_KEEPUP_OUT: printk("KEEPUP_OUT, "); break;
default:
printk("ERROR\n");
return(-1);
break;
}
}
switch(prot) {
case ISDN_TIMRU_PACKET_PPP:
case ISDN_TIMRU_PACKET_PPP_NO_HEADER:
if(prot == ISDN_TIMRU_PACKET_PPP) {
ppp_proto = PPP_PROTOCOL((char *)buf);
/*
cbuf = (char *)(buf + PPP_HDRLEN);
*/
} else {
ppp_proto = (int)arg;
/*
cbuf = (char *)buf;
*/
}
match_rule.protfam = ISDN_TIMRU_PROTFAM_PPP;
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_WILDCARD;
switch(ppp_proto) {
case PPP_IPCP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_IPCP;
VERBOSE_PRINTK(5, "", "PPP/IPCP\n");
break;
case PPP_IPXCP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_IPXCP;
VERBOSE_PRINTK(5, "", "PPP/IPXCP\n");
break;
case PPP_CCP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_CCP;
VERBOSE_PRINTK(5, "", "PPP/CCP\n");
break;
case PPP_LCP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_LCP;
VERBOSE_PRINTK(5, "", "PPP/LCP\n");
break;
case PPP_PAP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_PAP;
VERBOSE_PRINTK(5, "", "PPP/PAP\n");
break;
case PPP_LQR:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_LQR;
VERBOSE_PRINTK(5, "", "PPP/LQR\n");
break;
case PPP_CHAP:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_CHAP;
VERBOSE_PRINTK(5, "", "PPP/CHAP\n");
break;
default:
match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_WILDCARD;
if(dev->net_verbose >= 5) {
printk("PPP/? (%x)\n", ppp_proto);
#ifdef ISDN_DEBUG_NET_DUMP
isdn_dumppkt("R:", (u_char *)buf, 40, 40);
#endif
}
break;
}
break;
case ISDN_TIMRU_PACKET_SKB:
skb = (struct sk_buff *)buf;
#ifdef CONFIG_ISDN_PPP
if((type == ISDN_TIMRU_BRINGUP ||
type == ISDN_TIMRU_KEEPUP_OUT) &&
lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
/* jump over fake header. */
ppp_hdrlen = IPPP_MAX_HEADER;
}
#endif
switch(ntohs(skb->protocol)) {
case ETH_P_IP:
/*
if(!(ip = skb->ip_hdr))
*/
ip = (struct iphdr *)(skb->data + ppp_hdrlen);
match_rule.protfam = ISDN_TIMRU_PROTFAM_IP;
match_rule.rule.ip.saddr.s_addr = ip->saddr;
match_rule.rule.ip.daddr.s_addr = ip->daddr;
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
switch(ip->protocol) {
case IPPROTO_ICMP:
if(!(icmp = (struct icmphdr *)((unsigned long *)ip+ip->ihl))) {
VERBOSE_PRINTK(5, "", "IP/ICMP HDR-ERR\n");
} else {
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_ICMP;
match_rule.rule.ip.pt.type.from = icmp->type;
if(dev->net_verbose >= 5) {
printk("IP/ICMP ");
printk_ip(ip->saddr);
printk(" --> ");
printk_ip(ip->daddr);
printk("/");
printk_port(icmp->type);
printk("\n");
}
}
break;
case IPPROTO_IGMP:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
VERBOSE_PRINTK(5, "", "IP/IGMP\n");
break;
case IPPROTO_IPIP:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
VERBOSE_PRINTK(5, "", "IP/IPIP\n");
break;
case IPPROTO_TCP:
if(!(tcp = (struct tcphdr *)((unsigned long *)ip+ip->ihl))) {
VERBOSE_PRINTK(5, "", "IP/TCP HDR-ERR\n");
} else {
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_TCP;
match_rule.rule.ip.pt.port.s_from = tcp->source;
match_rule.rule.ip.pt.port.d_from = tcp->dest;
if(dev->net_verbose >= 5) {
printk("IP/TCP ");
printk_ip(ip->saddr);
printk("/");
printk_port(tcp->source);
printk(" --> ");
printk_ip(ip->daddr);
printk("/");
printk_port(tcp->dest);
printk("\n");
}
}
break;
case IPPROTO_EGP:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
VERBOSE_PRINTK(5, "", "IP/EGP\n");
break;
case IPPROTO_PUP:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
VERBOSE_PRINTK(5, "" "IP/PUP\n");
break;
case IPPROTO_UDP:
if(!(udp=(struct udphdr *)((unsigned long *)ip+ip->ihl))) {
VERBOSE_PRINTK(5, "", "IP/UDP HDR-ERR\n");
} else {
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_UDP;
match_rule.rule.ip.pt.port.s_from = udp->source;
match_rule.rule.ip.pt.port.d_from = udp->dest;
if(dev->net_verbose >= 5) {
printk("IP/UDP ");
printk_ip(ip->saddr);
printk("/");
printk_port(udp->source);
printk(" --> ");
printk_ip(ip->daddr);
printk("/");
printk_port(udp->dest);
printk("\n");
}
}
break;
case IPPROTO_IDP:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
VERBOSE_PRINTK(5, "", "IP/IDP\n");
break;
default:
match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
if(dev->net_verbose >= 5) {
printk("IP/? (%x)\n", ip->protocol);
#ifdef ISDN_DEBUG_NET_DUMP
isdn_dumppkt("R:", (u_char *)skb, skb->len, 180);
#endif
}
break;
}
break;
case ETH_P_ARP:
VERBOSE_PRINTK(5, "", "ARP/?\n");
break;
case ETH_P_IPX:
VERBOSE_PRINTK(5, "", "IPX/?\n");
break;
case ETH_P_802_2:
VERBOSE_PRINTK(5, "", "802.2/?\n");
break;
case ETH_P_802_3:
VERBOSE_PRINTK(5, "", "802.3/?\n");
break;
default:
if(dev->net_verbose >= 5) {
printk("?/? (%x)\n", ntohs(skb->protocol));
#ifdef ISDN_DEBUG_NET_DUMP
isdn_dumppkt("R:", (u_char *)skb, skb->len, 1800);
#endif
}
break;
}
#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
#ifdef CONFIG_IP_MASQUERADE
check_for_masq = 0;
m_saddr = m_daddr = (__u32)0;
m_sport = m_dport = (__u16)0;
m_prot = 0;
switch(match_rule.protfam) {
case ISDN_TIMRU_PROTFAM_IP:
m_saddr = match_rule.rule.ip.saddr.s_addr;
m_daddr = match_rule.rule.ip.daddr.s_addr;
switch(match_rule.rule.ip.protocol) {
case ISDN_TIMRU_IP_TCP:
m_prot = IPPROTO_TCP;
m_sport = match_rule.rule.ip.pt.port.s_from;
m_dport = match_rule.rule.ip.pt.port.d_from;
check_for_masq = 1;
break;
case ISDN_TIMRU_IP_UDP:
m_prot = IPPROTO_UDP;
m_sport = match_rule.rule.ip.pt.port.s_from;
m_dport = match_rule.rule.ip.pt.port.d_from;
check_for_masq = 1;
break;
#if 0
#ifdef CONFIG_IP_MASQUERADE_ICMP
case ISDN_TIMRU_IP_ICMP:
m_sport = match_rule.rule.ip.pt.type.from;
m_dport = 0;
check_for_masq = 1;
break;
#endif
#endif
}
break;
}
if(check_for_masq) {
masq = NULL;
switch(type) {
case ISDN_TIMRU_BRINGUP:
case ISDN_TIMRU_KEEPUP_OUT:
if((masq = ip_masq_in_get_2(m_prot, m_daddr, m_dport, m_saddr, m_sport))) {
match_rule.rule.ip.saddr.s_addr = m_saddr;
match_rule.rule.ip.daddr.s_addr = m_daddr;
switch(m_prot) {
case IPPROTO_TCP:
case IPPROTO_UDP:
match_rule.rule.ip.pt.port.s_from = m_sport;
match_rule.rule.ip.pt.port.d_from = m_dport;
break;
}
}
break;
case ISDN_TIMRU_KEEPUP_IN:
if((masq = ip_masq_in_get_2(m_prot, m_saddr, m_sport, m_daddr, m_dport))) {
match_rule.rule.ip.saddr.s_addr = m_daddr;
match_rule.rule.ip.daddr.s_addr = m_saddr;
switch(m_prot) {
case IPPROTO_TCP:
case IPPROTO_UDP:
match_rule.rule.ip.pt.port.s_from = m_sport;
match_rule.rule.ip.pt.port.d_from = m_dport;
break;
}
}
break;
}
if(masq && dev->net_verbose >= 5) {
printk(KERN_DEBUG "MASQ-TIMRU: ");
printk_ip(masq->maddr);
printk("/");
printk_port(masq->mport);
printk(": ");
printk_ip(masq->saddr);
printk("/");
printk_port(masq->sport);
printk(" --> ");
printk_ip(masq->daddr);
printk("/");
printk_port(masq->dport);
printk("\n");
}
}
#endif
#endif
break;
}
new_timeout = lp->onhtime;
if(prot && lp->timeout_rules) {
isdn_timeout_rule *head, *tor;
int pf, found_match, i;
pf = match_rule.protfam;
found_match = 0;
while(1) {
head = tor = lp->timeout_rules->timru[type][pf];
i = 0;
while(tor) {
if((isdn_timru_match(&match_rule, tor) > 0) ^ (tor->neg > 0)) {
found_match = 1;
new_timeout = tor->timeout;
}
if(found_match) {
#ifdef DEBUG_RULES
printk(KERN_DEBUG "Rule %d-%d-%d matches\n", type, pf, i);
#endif
break;
}
if(tor->next == head)
tor = NULL;
else {
tor = tor->next;
i++;
}
}
if(! found_match && pf != ISDN_TIMRU_PROTFAM_WILDCARD)
pf = ISDN_TIMRU_PROTFAM_WILDCARD;
else
break;
}
if(! found_match) {
new_timeout = lp->timeout_rules->defaults[type];
#ifdef DEBUG_RULES
printk("No rule matches: using default\n");
#endif
}
}
if(type == ISDN_TIMRU_BRINGUP) {
if(new_timeout > 0) {
lp->huptimeout = new_timeout;
lp->huptimer = 0;
}
} else {
if(new_timeout > lp->huptimeout
|| lp->huptimeout - lp->huptimer < new_timeout) {
lp->huptimeout = new_timeout;
lp->huptimer = 0;
}
}
return(new_timeout);
}
static int
isdn_timru_match(isdn_timeout_rule *this, isdn_timeout_rule *rule) {
if(this->protfam != rule->protfam)
return(0);
switch(rule->protfam) {
case ISDN_TIMRU_PROTFAM_WILDCARD:
return(1);
break;
case ISDN_TIMRU_PROTFAM_PPP:
if(rule->rule.ppp.protocol == ISDN_TIMRU_PPP_WILDCARD
|| rule->rule.ppp.protocol == this->rule.ppp.protocol)
return(1);
break;
case ISDN_TIMRU_PROTFAM_IP:
if((this->rule.ip.saddr.s_addr & rule->rule.ip.smask.s_addr) != rule->rule.ip.saddr.s_addr
|| (this->rule.ip.daddr.s_addr & rule->rule.ip.dmask.s_addr) != rule->rule.ip.daddr.s_addr)
return(0);
if(rule->rule.ip.protocol == ISDN_TIMRU_IP_WILDCARD)
return(1);
if(rule->rule.ip.protocol != this->rule.ip.protocol)
return(0);
switch(rule->rule.ip.protocol) {
case ISDN_TIMRU_IP_ICMP:
if(this->rule.ip.pt.type.from < rule->rule.ip.pt.type.from
|| this->rule.ip.pt.type.from > rule->rule.ip.pt.type.to)
return(0);
break;
case ISDN_TIMRU_IP_TCP:
case ISDN_TIMRU_IP_UDP:
if(this->rule.ip.pt.port.s_from < rule->rule.ip.pt.port.s_from
|| this->rule.ip.pt.port.s_from > rule->rule.ip.pt.port.s_to)
return(0);
if(this->rule.ip.pt.port.d_from < rule->rule.ip.pt.port.d_from
|| this->rule.ip.pt.port.d_from > rule->rule.ip.pt.port.d_to)
return(0);
break;
}
break;
}
return(1);
}
static int
isdn_timru_rule_equals(isdn_timeout_rule *this, isdn_timeout_rule *rule) {
if(this->neg != rule->neg
|| this->protfam != rule->protfam)
return(0);
switch(rule->protfam) {
case ISDN_TIMRU_PROTFAM_PPP:
if(this->rule.ppp.protocol != rule->rule.ppp.protocol)
return(0);
break;
case ISDN_TIMRU_PROTFAM_IP:
if(this->rule.ip.protocol != rule->rule.ip.protocol
|| this->rule.ip.saddr.s_addr != rule->rule.ip.saddr.s_addr
|| this->rule.ip.smask.s_addr != rule->rule.ip.smask.s_addr
|| this->rule.ip.daddr.s_addr != rule->rule.ip.daddr.s_addr
|| this->rule.ip.dmask.s_addr != rule->rule.ip.dmask.s_addr)
return(0);
switch(rule->rule.ip.protocol) {
case ISDN_TIMRU_IP_ICMP:
if(this->rule.ip.pt.type.from != rule->rule.ip.pt.type.from
|| this->rule.ip.pt.type.to != rule->rule.ip.pt.type.to)
return(0);
break;
case ISDN_TIMRU_IP_TCP:
case ISDN_TIMRU_IP_UDP:
if(this->rule.ip.pt.port.s_from != rule->rule.ip.pt.port.s_from
|| this->rule.ip.pt.port.s_to != rule->rule.ip.pt.port.s_to
|| this->rule.ip.pt.port.d_from != rule->rule.ip.pt.port.d_from
|| this->rule.ip.pt.port.d_to != rule->rule.ip.pt.port.d_to)
return(0);
break;
}
break;
}
return(1);
}
int
isdn_timru_alloc_timeout_rules(struct device *ndev) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
int i, j;
ulong flags;
save_flags(flags);
cli();
if(!(lp->timeout_rules = (struct isdn_timeout_rules *)kmalloc(sizeof(struct isdn_timeout_rules), GFP_KERNEL))) {
restore_flags(flags);
printk(KERN_WARNING "isdn_timru: failed to allocate memory.\n");
return(-ENOMEM);
}
memset((char *)lp->timeout_rules, 0, sizeof(struct isdn_timeout_rules));
for(i = 0; i < ISDN_TIMRU_NUM_CHECK; i++) {
lp->timeout_rules->defaults[i] = lp->onhtime;
for(j = 0; j < ISDN_TIMRU_NUM_PROTFAM; j++)
lp->timeout_rules->timru[i][j] = NULL;
}
restore_flags(flags);
return(0);
}
int
isdn_timru_free_timeout_rules(struct device *ndev) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
isdn_timeout_rule *head, *this, *next;
int i, j;
ulong flags;
if(!lp->timeout_rules)
return(-1);
save_flags(flags);
cli();
for(i = 0; i < ISDN_TIMRU_NUM_CHECK; i++)
for(j = 0; j < ISDN_TIMRU_NUM_CHECK; j++)
if((head = lp->timeout_rules->timru[i][j])) {
this = head;
do {
next = this->next;
kfree(this);
} while(next == head);
}
kfree(lp->timeout_rules);
lp->timeout_rules = NULL;
restore_flags(flags);
return(0);
}
int
isdn_timru_add_rule(int where, struct device *ndev, isdn_timeout_rule *rule) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
isdn_timeout_rule **head;
ulong flags;
int ret;
if(!lp->timeout_rules)
if((ret = isdn_timru_alloc_timeout_rules(ndev)))
return(ret);
if(rule->timeout < 0)
return(-EINVAL);
save_flags(flags);
cli();
head = &(lp->timeout_rules->timru[rule->type][rule->protfam]);
if(! *head)
rule->next = rule->prev = *head = rule;
else {
rule->next = *head;
rule->prev = (*head)->prev;
(*head)->prev->next = rule;
(*head)->prev = rule;
if(where == 0) /* add to head of chain */
*head = rule;
}
restore_flags(flags);
return(0);
}
int
isdn_timru_del_rule(struct device *ndev, isdn_timeout_rule *rule) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
isdn_timeout_rule **head, *this;
ulong flags;
if(!lp->timeout_rules)
return(-EINVAL);
save_flags(flags);
cli();
head = &(lp->timeout_rules->timru[rule->type][rule->protfam]);
if(! *head) {
restore_flags(flags);
return(-EINVAL);
}
this = *head;
do {
if(isdn_timru_rule_equals(this, rule)) {
if(this->next != this) { /* more than one rule */
this->prev->next = this->next;
this->next->prev = this->prev;
if(this == *head)
*head = this->next;
} else
*head = NULL;
kfree(this);
restore_flags(flags);
return(0);
} else
this = this->next;
} while(this == *head);
restore_flags(flags);
return(-EINVAL);
}
int
isdn_timru_set_default(int type, struct device *ndev, int def) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
ulong flags;
int ret;
if(!lp->timeout_rules)
if((ret = isdn_timru_alloc_timeout_rules(ndev)))
return(ret);
if(def < 0)
return(-EINVAL);
save_flags(flags);
cli();
lp->timeout_rules->defaults[type] = def;
restore_flags(flags);
return(0);
}
int
isdn_timru_get_rule(struct device *ndev, isdn_timeout_rule **rule, int i, int j, int k) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
isdn_timeout_rule *head, *this;
int l;
ulong flags;
if(!lp->timeout_rules
|| i < 0 || i > ISDN_TIMRU_NUM_CHECK
|| j < 0 || j > ISDN_TIMRU_NUM_PROTFAM
|| k < 0)
return(-EINVAL);
save_flags(flags);
cli();
if(!(this = head = lp->timeout_rules->timru[i][j])) {
restore_flags(flags);
return(-EINVAL);
}
for(l = 0; l < k; l++) {
if(this->next == head) {
restore_flags(flags);
return(-EINVAL);
}
this = this->next;
}
*rule = this;
restore_flags(flags);
return(0);
}
int
isdn_timru_get_default(int type, struct device *ndev, int *ret) {
isdn_net_local *lp = (isdn_net_local *)ndev->priv;
ulong flags;
if(!lp->timeout_rules)
return(-EINVAL);
save_flags(flags);
cli();
*ret = lp->timeout_rules->defaults[type];
restore_flags(flags);
return(0);
}
int
isdn_timru_ioctl_add_rule(isdn_ioctl_timeout_rule *iorule)
{
isdn_net_dev *p = isdn_net_findif(iorule->name);
isdn_timeout_rule *r;
if(p) {
if(iorule->where < 0) { /* set default */
return(isdn_timru_set_default(iorule->type, &p->dev, iorule->defval));
} else {
if(!(r = (isdn_timeout_rule *) kmalloc(sizeof(isdn_timeout_rule), GFP_KERNEL)))
return(-ENOMEM);
memcpy((char *)r, (char *)&iorule->rule, sizeof(isdn_timeout_rule));
return(isdn_timru_add_rule(iorule->where, &p->dev, r));
}
}
return(-ENODEV);
}
int
isdn_timru_ioctl_del_rule(isdn_ioctl_timeout_rule *iorule)
{
isdn_net_dev *p = isdn_net_findif(iorule->name);
isdn_timeout_rule *r;
if(p) {
if(!(r = (isdn_timeout_rule *) kmalloc(sizeof(isdn_timeout_rule), GFP_KERNEL)))
return(-ENOMEM);
memcpy((char *)r, (char *)&iorule->rule, sizeof(isdn_timeout_rule));
return(isdn_timru_del_rule(&p->dev, r));
}
return(-ENODEV);
}
int
isdn_timru_ioctl_get_rule(isdn_ioctl_timeout_rule *iorule)
{
int ret, def;
isdn_net_dev *p = isdn_net_findif(iorule->name);
isdn_timeout_rule *r;
if(p) {
if(iorule->where < 0) { /* get default */
if((ret = isdn_timru_get_default(iorule->type, &p->dev, &def)) < 0)
return(ret);
iorule->protfam = p->local->huptimer;
iorule->index = p->local->huptimeout;
iorule->defval = def;
} else {
if(isdn_timru_get_rule(&p->dev, &r, iorule->type, iorule->protfam, iorule->index))
return(-ENOMEM);
memcpy((char *)&iorule->rule, (char *)r, sizeof(isdn_timeout_rule));
}
return(0);
}
return(-ENODEV);
}
#endif