Merge branch 'master' into encryption
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 6a454bf..8c5547d 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -499,6 +499,7 @@
 int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, 
 			    u_int8_t ta);
 int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
+int rsl_encryption_cmd(struct msgb *msg);
 int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
 		   u_int8_t *ms_ident, u_int8_t chan_needed);
 int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_needed,
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 185d760..acca100 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -720,6 +720,7 @@
 int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len);
 
 int gsm48_send_rr_release(struct gsm_lchan *lchan);
+int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan);
 int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
 			   u_int8_t apdu_len, u_int8_t *apdu);
 
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 4bdca2a..1ceb105 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -126,6 +126,9 @@
 	int waiting_for_imei : 1;
 };
 
+#define MAX_A5_KEY_LEN	(128/8)
+#define RSL_ENC_ALG_A5(x)	(x+1)
+
 struct gsm_lchan {
 	/* The TS that we're part of */
 	struct gsm_bts_trx_ts *ts;
@@ -140,6 +143,12 @@
 	/* Power levels for MS and BTS */
 	u_int8_t bs_power;
 	u_int8_t ms_power;
+	/* Encryption information */
+	struct {
+		u_int8_t alg_id;
+		u_int8_t key_len;
+		u_int8_t key[MAX_A5_KEY_LEN];
+	} encr;
 	
 	/* To whom we are allocated at the moment */
 	struct gsm_subscriber *subscr;
@@ -365,6 +374,7 @@
 	char *name_long;
 	char *name_short;
 	enum gsm_auth_policy auth_policy;
+	int a5_encryption;
 
 	/* layer 4 */
 	int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 7be1a6b..800f202 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -317,6 +317,16 @@
 		memset(out+len, 0x2b, MACBLOCK_SIZE-len);
 }
 
+/* Chapter 9.3.7: Encryption Information */
+static int build_encr_info(u_int8_t *out, struct gsm_lchan *lchan)
+{
+	*out++ = lchan->encr.alg_id & 0xff;
+	if (lchan->encr.key_len)
+		memcpy(out, lchan->encr.key, lchan->encr.key_len);
+	return lchan->encr.key_len + 1;
+}
+
+
 static const char *rsl_err_vals[0xff] = {
 	[RSL_ERR_RADIO_IF_FAIL] =	"Radio Interface Failure",
 	[RSL_ERR_RADIO_LINK_FAIL] =	"Radio Link Failure",
@@ -587,10 +597,14 @@
 		     (u_int8_t *) &cm);
 	msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
 		     (u_int8_t *) &ci);
-#if 0
-	msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
-		     (u_int8_t *) &encr_info);
-#endif
+
+	if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
+		u_int8_t encr_info[MAX_A5_KEY_LEN+2];
+		rc = build_encr_info(encr_info, lchan);
+		if (rc > 0)
+			msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
+	}
+
 	msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
 	msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
 	msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
@@ -621,10 +635,45 @@
 
 	msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
 		     (u_int8_t *) &cm);
-#if 0
-	msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
-		     (u_int8_t *) &encr_info);
-#endif
+
+	if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
+		u_int8_t encr_info[MAX_A5_KEY_LEN+2];
+		rc = build_encr_info(encr_info, lchan);
+		if (rc > 0)
+			msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
+	}
+
+	msg->trx = lchan->ts->trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 8.4.6: Send the encryption command with given L3 info */
+int rsl_encryption_cmd(struct msgb *msg)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct gsm_lchan *lchan = msg->lchan;
+	u_int8_t chan_nr = lchan2chan_nr(lchan);
+	u_int8_t encr_info[MAX_A5_KEY_LEN+2];
+	u_int8_t l3_len = msg->len;
+	int rc;
+
+	/* First push the L3 IE tag and length */
+	msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
+
+	/* then the link identifier (SAPI0, main sign link) */
+	msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0);
+
+	/* then encryption information */
+	rc = build_encr_info(encr_info, lchan);
+	if (rc <= 0)
+		return rc;
+	msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info);
+
+	/* and finally the DCHAN header */
+	dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_ENCR_CMD);
+	dh->chan_nr = chan_nr;
 
 	msg->trx = lchan->ts->trx;
 
@@ -1082,6 +1131,7 @@
 	arfcn = lchan->ts->trx->arfcn;
 	subch = lchan->nr;
 	
+	lchan->encr.alg_id = RSL_ENC_ALG_A5(0);	/* no encryption */
 	lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
 	lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
 	lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index dc85670..d6fe09f 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1674,6 +1674,10 @@
 	case GSM48_MT_RR_APP_INFO:
 		rc = gsm48_rx_rr_app_info(msg);
 		break;
+	case GSM48_MT_RR_CIPH_M_COMPL:
+		DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
+		/* FIXME: check for MI (if any) */
+		break;
 	default:
 		fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
 			gh->msg_type);
@@ -1683,6 +1687,29 @@
 	return rc;
 }
 
+/* Chapter 9.1.9: Ciphering Mode Command */
+int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan)
+{
+	struct msgb *msg = gsm48_msgb_alloc();
+	struct gsm48_hdr *gh;
+	u_int8_t ciph_mod_set;
+
+	msg->lchan = lchan;
+
+	DEBUGP(DRR, "TX CIPHERING MODE CMD\n");
+
+	if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0))
+		ciph_mod_set = 0;
+	else
+		ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1;
+
+	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+	gh->proto_discr = GSM48_PDISC_RR;
+	gh->msg_type = GSM48_MT_RR_CIPH_M_CMD;
+	gh->data[0] = 0x10 | (ciph_mod_set & 0xf);
+
+	return rsl_encryption_cmd(msg);
+}
 
 int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
 			   u_int8_t apdu_len, u_int8_t *apdu)
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 248e68e..f75cfaf 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -85,6 +85,8 @@
 		net->name_short, VTY_NEWLINE);
 	vty_out(vty, "  Authentication policy: %s%s",
 		gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
+	vty_out(vty, "  Encryption: A5/%u%s", net->a5_encryption,
+		VTY_NEWLINE);
 }
 
 DEFUN(show_net, show_net_cmd, "show network",
@@ -264,6 +266,7 @@
 	vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
 	vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
 	vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
+	vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
 
 	return CMD_SUCCESS;
 }
@@ -766,6 +769,16 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_net_encryption,
+      cfg_net_encryption_cmd,
+      "encryption a5 (0|1|2)",
+      "Enable or disable encryption (A5) for this network\n")
+{
+	gsmnet->auth_policy = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
 /* per-BTS configuration */
 DEFUN(cfg_bts,
       cfg_bts_cmd,
@@ -1155,6 +1168,7 @@
 	install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
 	install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
 	install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
+	install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
 
 	install_element(GSMNET_NODE, &cfg_bts_cmd);
 	install_node(&bts_node, config_write_bts);