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) {
K
Kozlov Dmitry 已提交
80 81
		log_ppp_info("send ");
		rad_packet_print(reply, log_ppp_info);
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) {
K
Kozlov Dmitry 已提交
115 116
		log_ppp_info("send ");
		rad_packet_print(reply, log_ppp_info);
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
	if (conf_verbose) {
K
Kozlov Dmitry 已提交
130 131
		log_ppp_info("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_info);
132 133
	}

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
	if (conf_verbose) {
K
Kozlov Dmitry 已提交
150 151
		log_ppp_info("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_info);
152
	}
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
{
	struct rad_packet_t *pack;
	struct radius_pd_t *rpd;
	int err_code;
	struct sockaddr_in addr;

K
Kozlov Dmitry 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
	while (1) {
		if (rad_packet_recv(h->fd, &pack, &addr))
			return 0;

		if (!pack)
			continue;

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

		if (dm_coa_check_RA(pack, conf_dm_coa_secret)) {
			log_warn("radius:dm_coa: RA validation failed\n");
			goto out_err_no_reply;
		}

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

		if (rad_check_nas_pack(pack)) {
			log_warn("radius:dm_coa: NAS identification failed\n");
			err_code = 403;
			goto out_err;
		}
		
		rpd = rad_find_session_pack(pack);
		if (!rpd) {
			log_warn("radius:dm_coa: session not found\n");
			err_code = 503;
			goto out_err;
		}
		
		if (rpd->dm_coa_req) {
			pthread_mutex_unlock(&rpd->lock);
			goto out_err_no_reply;
		}

		rpd->dm_coa_req = pack;
		memcpy(&rpd->dm_coa_addr, &addr, sizeof(addr));

		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);
222

223
		pthread_mutex_unlock(&rpd->lock);
224

K
Kozlov Dmitry 已提交
225
		continue;
226

K
Kozlov Dmitry 已提交
227 228
	out_err:
		dm_coa_send_nak(h->fd, pack, &addr, err_code);
229

K
Kozlov Dmitry 已提交
230 231 232
	out_err_no_reply:
		rad_packet_free(pack);
	}
233 234
}

235
static void dm_coa_close(struct triton_context_t *ctx)
236
{
237
	struct dm_coa_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
238 239
	triton_md_unregister_handler(&serv->hnd);
	close(serv->hnd.fd);
K
Kozlov Dmitry 已提交
240
	triton_context_unregister(ctx);
241 242
}

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

static void __init init(void)
{
251
	struct sockaddr_in addr;
252 253 254

	serv.hnd.fd = socket (PF_INET, SOCK_DGRAM, 0);
  if (serv.hnd.fd < 0) {
255
    log_emerg("radius:dm_coa: socket: %s\n", strerror(errno));
256 257 258 259 260 261 262 263 264
    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) {
265
    log_emerg("radius:dm_coa: bind: %s\n", strerror(errno));
266 267 268 269 270
		close(serv.hnd.fd);
    return;
  }

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