dm_coa.c 5.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/md5.h>

#include "triton.h"
15
#include "events.h"
16 17
#include "log.h"

18
#include "radius_p.h"
19

D
Dmitry Kozlov 已提交
20 21
#include "memdebug.h"

22 23
#define PD_COA_PORT 3799

24
struct dm_coa_serv_t
25 26 27 28 29
{
	struct triton_context_t ctx;
	struct triton_md_handler_t hnd;
};

30 31
static struct dm_coa_serv_t serv;

32
static int dm_coa_check_RA(struct rad_packet_t *pack, const char *secret)
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
{
	uint8_t RA[16];
	MD5_CTX ctx;

	memset(RA, 0, 16);
	
	MD5_Init(&ctx);
	MD5_Update(&ctx, pack->buf, 4);
	MD5_Update(&ctx, RA, 16);
	MD5_Update(&ctx, pack->buf + 20, pack->len - 20);
	MD5_Update(&ctx, secret, strlen(secret));
	MD5_Final(RA, &ctx);

	return memcmp(RA, pack->buf + 4, 16);
}

49
static void dm_coa_set_RA(struct rad_packet_t *pack, const char *secret)
50 51 52 53 54 55 56 57 58
{
	MD5_CTX ctx;

	MD5_Init(&ctx);
	MD5_Update(&ctx, pack->buf, pack->len);
	MD5_Update(&ctx, secret, strlen(secret));
	MD5_Final(pack->buf + 4, &ctx);
}

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
static int dm_coa_send_ack(int fd, struct rad_packet_t *req, struct sockaddr_in *addr)
{
	struct rad_packet_t *reply;
	uint8_t RA[16];

	memcpy(RA, req->buf + 4, sizeof(RA));

	reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_ACK : CODE_DISCONNECT_ACK);
	if (!reply)
		return -1;

	reply->id = req->id;
	
	if (rad_packet_build(reply, RA)) {
		rad_packet_free(reply);
		return -1;
	}

	dm_coa_set_RA(reply, conf_dm_coa_secret);

	if (conf_verbose) {
80 81
		log_ppp_debug("send ");
		rad_packet_print(reply, log_ppp_debug);
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	}

	rad_packet_send(reply, fd, addr);
	
	rad_packet_free(reply);

	return 0;
}

static int dm_coa_send_nak(int fd, struct rad_packet_t *req, struct sockaddr_in *addr, int err_code)
{
	struct rad_packet_t *reply;
	uint8_t RA[16];

	memcpy(RA, req->buf + 4, sizeof(RA));

	reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_NAK : CODE_DISCONNECT_NAK);
	if (!reply)
		return -1;

	reply->id = req->id;

104 105
	if (err_code)
		rad_packet_add_int(reply, "Error-Cause", err_code);
106 107 108 109 110 111 112 113 114

	if (rad_packet_build(reply, RA)) {
		rad_packet_free(reply);
		return -1;
	}

	dm_coa_set_RA(reply, conf_dm_coa_secret);

	if (conf_verbose) {
115 116
		log_ppp_debug("send ");
		rad_packet_print(reply, log_ppp_debug);
117 118 119 120 121 122 123 124 125 126
	}

	rad_packet_send(reply, fd, addr);
	
	rad_packet_free(reply);

	return 0;
}


127 128
static void disconnect_request(struct radius_pd_t *rpd)
{
129 130 131 132 133
	if (conf_verbose) {
		log_ppp_debug("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_debug);
	}

134 135
	dm_coa_send_ack(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr);

136 137
	rad_packet_free(rpd->dm_coa_req);
	rpd->dm_coa_req = NULL;
138 139 140 141 142 143

	ppp_terminate(rpd->ppp, 0);
}

static void coa_request(struct radius_pd_t *rpd)
{
144 145 146 147 148
	struct ev_radius_t ev = {
		.ppp = rpd->ppp,
		.request = rpd->dm_coa_req,
	};

149 150 151 152
	if (conf_verbose) {
		log_ppp_debug("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_debug);
	}
153 154 155 156 157 158 159

	triton_event_fire(EV_RADIUS_COA, &ev);

	if (ev.res)
		dm_coa_send_nak(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr, 0);
	else
		dm_coa_send_ack(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr);
160
	
161
	rad_packet_free(rpd->dm_coa_req);
162 163

	pthread_mutex_lock(&rpd->lock);
164
	rpd->dm_coa_req = NULL;
165
	pthread_mutex_unlock(&rpd->lock);
166 167
}

168
static int dm_coa_read(struct triton_md_handler_t *h)
169 170 171 172 173 174 175 176 177 178 179 180
{
	struct rad_packet_t *pack;
	struct radius_pd_t *rpd;
	int err_code;
	struct sockaddr_in addr;


	pack = rad_packet_recv(h->fd, &addr);
	if (!pack)
		return 0;

	if (pack->code != CODE_DISCONNECT_REQUEST	&& pack->code != CODE_COA_REQUEST) {
181
		log_warn("radius:dm_coa: unexpected code (%i) received\n", pack->code);
182 183 184
		goto out_err_no_reply;
	}

185 186
	if (dm_coa_check_RA(pack, conf_dm_coa_secret)) {
		log_warn("radius:dm_coa: RA validation failed\n");
187 188 189 190 191 192 193 194 195
		goto out_err_no_reply;
	}

	if (conf_verbose) {
		log_debug("recv ");
		rad_packet_print(pack, log_debug);
	}

	if (rad_check_nas_pack(pack)) {
196
		log_warn("radius:dm_coa: NAS identification failed\n");
197 198 199 200 201 202
		err_code = 403;
		goto out_err;
	}
	
	rpd = rad_find_session_pack(pack);
	if (!rpd) {
203
		log_warn("radius:dm_coa: session not found\n");
204 205 206 207
		err_code = 503;
		goto out_err;
	}
	
208 209 210 211 212
	if (rpd->dm_coa_req) {
		pthread_mutex_unlock(&rpd->lock);
		goto out_err_no_reply;
	}

213
	rpd->dm_coa_req = pack;
214
	memcpy(&rpd->dm_coa_addr, &addr, sizeof(addr));
215 216 217 218 219 220 221 222 223 224 225

	if (pack->code == CODE_DISCONNECT_REQUEST)
		triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))disconnect_request, rpd);
	else
		triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))coa_request, rpd);

	pthread_mutex_unlock(&rpd->lock);

	return 0;

out_err:
226
	dm_coa_send_nak(h->fd, pack, &addr, err_code);
227 228 229 230 231 232

out_err_no_reply:
	rad_packet_free(pack);
	return 0;
}

233
static void dm_coa_close(struct triton_context_t *ctx)
234
{
235
	struct dm_coa_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
236 237 238 239
	triton_md_unregister_handler(&serv->hnd);
	close(serv->hnd.fd);
}

240 241
static struct dm_coa_serv_t serv = {
	.ctx.close = dm_coa_close,
242
	.ctx.before_switch = log_switch,
243
	.hnd.read = dm_coa_read,
244 245 246 247 248 249 250 251
};

static void __init init(void)
{
  struct sockaddr_in addr;

	serv.hnd.fd = socket (PF_INET, SOCK_DGRAM, 0);
  if (serv.hnd.fd < 0) {
252
    log_error("radius:dm_coa: socket: %s\n", strerror(errno));
253 254 255 256 257 258 259 260 261
    return;
  }
  addr.sin_family = AF_INET;
  addr.sin_port = htons (PD_COA_PORT);
	if (conf_nas_ip_address)
	  addr.sin_addr.s_addr = inet_addr(conf_nas_ip_address);
	else
		addr.sin_addr.s_addr = htonl (INADDR_ANY);
  if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
262
    log_error("radius:dm_coa: bind: %s\n", strerror(errno));
263 264 265 266 267
		close(serv.hnd.fd);
    return;
  }

	if (fcntl(serv.hnd.fd, F_SETFL, O_NONBLOCK)) {
268
    log_error("radius:dm_coa: failed to set nonblocking mode: %s\n", strerror(errno));
269 270 271 272
		close(serv.hnd.fd);
    return;
	}
	
273
	triton_context_register(&serv.ctx, NULL);
274 275
	triton_md_register_handler(&serv.ctx, &serv.hnd);
	triton_md_enable_handler(&serv.hnd, MD_MODE_READ);
K
Kozlov Dmitry 已提交
276
	triton_context_wakeup(&serv.ctx);
277
}